Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ACP-4499: Added console command for post-install setup #20

Merged
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"php": ">=8.2",
"guzzlehttp/psr7": "^2.7",
"league/openapi-psr7-validator": "^0.22.0",
"spryker/app-kernel-extension": "^1.0.0",
"spryker/app-kernel-extension": "^1.1.0",
"spryker/glue-application-extension": "^1.0.0",
"spryker/kernel": "^3.30.0",
"spryker/log": "^3.0.0",
Expand Down
2 changes: 2 additions & 0 deletions resources/api/asyncapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ components:
type: boolean
config:
type: array
messageChannels:
type: array
required:
- appIdentifier
- status
Expand Down
62 changes: 41 additions & 21 deletions src/Spryker/Glue/AppKernel/Validator/BodyStructureValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Symfony\Component\Validator\Constraints\Required;
use Symfony\Component\Validator\Constraints\Type;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Throwable;

class BodyStructureValidator implements RequestValidatorInterface
{
Expand All @@ -35,38 +36,57 @@ public function validate(GlueRequestTransfer $glueRequestTransfer): GlueRequestV
->setIsValid(true)
->setStatus(Response::HTTP_OK);

$content = $this->appKernelToUtilEncodingService->decodeJson((string)$glueRequestTransfer->getContent(), true);
$constraintViolationList = $this->validator->validate($content, $this->getConstraintForRequestStructure());
try {
$content = $this->appKernelToUtilEncodingService->decodeJson((string)$glueRequestTransfer->getContent(), true);
} catch (Throwable $throwable) {
return $this->getFailedGlueRequestValidationTransfer($glueRequestValidationTransfer);
}

if ($constraintViolationList->count() > 0) {
$glueRequestValidationTransfer
->setIsValid(false)
->setStatus(Response::HTTP_UNPROCESSABLE_ENTITY);
$constraintViolationList = $this->validator->validate(['content' => $content], $this->getConstraintForRequestStructure());

$glueRequestValidationTransfer->addError(
(new GlueErrorTransfer())
->setCode((string)Response::HTTP_UNPROCESSABLE_ENTITY)
->setMessage(AppKernelConfig::RESPONSE_MESSAGE_VALIDATION_FORMAT_ERROR_MESSAGE),
);
if ($constraintViolationList->count() > 0) {
return $this->getFailedGlueRequestValidationTransfer($glueRequestValidationTransfer);
}

return $glueRequestValidationTransfer;
}

protected function getFailedGlueRequestValidationTransfer(
GlueRequestValidationTransfer $glueRequestValidationTransfer
): GlueRequestValidationTransfer {
$glueRequestValidationTransfer
->setIsValid(false)
->setStatus(Response::HTTP_UNPROCESSABLE_ENTITY);

$glueRequestValidationTransfer->addError(
(new GlueErrorTransfer())
->setCode((string)Response::HTTP_UNPROCESSABLE_ENTITY)
->setMessage(AppKernelConfig::RESPONSE_MESSAGE_VALIDATION_FORMAT_ERROR_MESSAGE),
);

return $glueRequestValidationTransfer;
}

protected function getConstraintForRequestStructure(): Collection
{
return new Collection([
'data' => new Collection([
'type' => new EqualTo(AppKernelConfig::REQUEST_DATA_TYPE),
'attributes' => new Collection([
'configuration' => [
new Required(),
new NotBlank(),
new Type(['type' => 'string']),
new Json(),
],
'content' => [
stereomon marked this conversation as resolved.
Show resolved Hide resolved
new Required(),
new NotBlank(),
new Collection([
'data' => new Collection([
'type' => new EqualTo(AppKernelConfig::REQUEST_DATA_TYPE),
'attributes' => new Collection([
'configuration' => [
new Required(),
new NotBlank(),
new Type(['type' => 'string']),
new Json(),
],
]),
]),
]),
]),
],
]);
}
}
12 changes: 11 additions & 1 deletion src/Spryker/Shared/AppKernel/AppKernelConstants.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,17 @@ class AppKernelConstants

/**
* Specification:
* - Identifier of the application.
* - List of message channels that are used by the application.
*
* @api
*
* @var string
*/
public const APP_MESSAGE_CHANNELS = 'APP:APP_MESSAGE_CHANNELS';

/**
* Specification:
* - Version of the application.
*
* @api
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
<transfer name="AppConfigUpdated" strict="true">
<property name="status" type="bool" description="The Status of an App that enabled (1) or disabled (0)"/>
<property name="isActive" type="bool" description="The Status of an App that enabled (1) or disabled (0)"/>
<property name="config" type="string"/>
<property name="config" type="array" singular="config"/>
<property name="appIdentifier" type="string" description="The identifier of an App"/>
<property name="messageChannels" type="array" singular="messageChannel" description="The message channels that are used by an App"/>
<property name="messageAttributes" type="MessageAttributes"/>
</transfer>

Expand Down Expand Up @@ -54,6 +55,7 @@

<transfer name="AppConfigCriteria" strict="true">
<property name="tenantIdentifier" type="string"/>
<property name="isActive" type="bool"/>
</transfer>

<transfer name="AppDisconnectResponse" strict="true">
Expand Down
14 changes: 14 additions & 0 deletions src/Spryker/Zed/AppKernel/AppKernelConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace Spryker\Zed\AppKernel;

use Spryker\Shared\AppKernel\AppKernelConstants;
use Spryker\Shared\MessageBroker\MessageBrokerConstants;
use Spryker\Zed\Kernel\AbstractBundleConfig;

class AppKernelConfig extends AbstractBundleConfig
Expand All @@ -34,4 +35,17 @@ public function getAppIdentifier(): string
{
return $this->get(AppKernelConstants::APP_IDENTIFIER);
}

/**
* @api
*
* @return list<string>
*/
public function getAppMessageChannels(): array
{
return $this->get(
AppKernelConstants::APP_MESSAGE_CHANNELS,
array_unique(array_values($this->get(MessageBrokerConstants::MESSAGE_TO_CHANNEL_MAP))),
);
}
}
29 changes: 29 additions & 0 deletions src/Spryker/Zed/AppKernel/AppKernelDependencyProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ class AppKernelDependencyProvider extends AbstractBundleDependencyProvider
*/
public const PLUGIN_CONFIGURATION_AFTER_DELETE_PLUGINS = 'APP_KERNEL:PLUGIN_CONFIGURATION_AFTER_DELETE_PLUGINS';

/**
* @var string
*/
public const PLUGINS_POST_INSTALL_TASK = 'APP_KERNEL:PLUGINS_POST_INSTALL_TASK';

public function provideBusinessLayerDependencies(Container $container): Container
{
$container = parent::provideBusinessLayerDependencies($container);
Expand All @@ -84,6 +89,13 @@ public function provideBusinessLayerDependencies(Container $container): Containe
return $container;
}

public function provideCommunicationLayerDependencies(Container $container): Container
{
$container = parent::provideCommunicationLayerDependencies($container);

return $this->addPostInstallTaskPlugins($container);
}

public function providePersistenceLayerDependencies(Container $container): Container
{
$container = parent::providePersistenceLayerDependencies($container);
Expand Down Expand Up @@ -219,4 +231,21 @@ protected function getConfigurationAfterDeletePlugins(): array
{
return [];
}

protected function addPostInstallTaskPlugins(Container $container): Container
{
$container->set(static::PLUGINS_POST_INSTALL_TASK, function (): array {
return $this->getPostInstallTaskPlugins();
});

return $container;
}

/**
* @return list<\Spryker\Zed\AppKernelExtension\Dependency\Plugin\PostInstallTaskPluginInterface>
*/
protected function getPostInstallTaskPlugins(): array
{
return [];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public function sendAppConfigUpdatedMessage(AppConfigTransfer $appConfigTransfer
$appConfigUpdatedTransfer = new AppConfigUpdatedTransfer();
$appConfigUpdatedTransfer->fromArray($appConfigTransfer->toArray(), true);
$appConfigUpdatedTransfer
->setMessageChannels($this->appKernelConfig->getAppMessageChannels())
->setAppIdentifier($this->appKernelConfig->getAppIdentifier());

$appConfigUpdatedTransfer->setMessageAttributes($this->getMessageAttributes(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

namespace Spryker\Zed\AppKernel\Communication;

use Spryker\Zed\AppKernel\AppKernelDependencyProvider;
use Spryker\Zed\Kernel\Communication\AbstractCommunicationFactory;

/**
* @method \Spryker\Zed\AppKernel\Business\AppKernelFacadeInterface getFacade()
* @method \Spryker\Zed\AppKernel\AppKernelConfig getConfig()
* @method \Spryker\Zed\AppKernel\Persistence\AppKernelRepositoryInterface getRepository()
* @method \Spryker\Zed\AppKernel\Persistence\AppKernelEntityManagerInterface getEntityManager()
*/
class AppKernelCommunicationFactory extends AbstractCommunicationFactory
{
/**
* @return list<\Spryker\Zed\AppKernelExtension\Dependency\Plugin\PostInstallTaskPluginInterface>
*/
public function getPostInstallTaskPlugins(): array
{
return $this->getProvidedDependency(AppKernelDependencyProvider::PLUGINS_POST_INSTALL_TASK);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

namespace Spryker\Zed\AppKernel\Communication\Console;

use Spryker\Zed\Kernel\Communication\Console\Console;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
* @method \Spryker\Zed\AppKernel\Communication\AppKernelCommunicationFactory getFactory()
* @method \Spryker\Zed\AppKernel\Business\AppKernelFacadeInterface getFacade()
* @method \Spryker\Zed\AppKernel\Persistence\AppKernelRepositoryInterface getRepository()
*/
class PostInstallTasksConsole extends Console
{
/**
* @var string
*/
public const COMMAND_NAME = 'setup:post-install';

/**
* @var string
*/
public const DESCRIPTION = "Executes post install tasks.\nDespite the name, this command is not intended to be used in Spryker post-deploy hook `SPRYKER_HOOK_AFTER_DEPLOY` as it is not executed in the context of the application.";

protected function configure(): void
{
$this->setName(static::COMMAND_NAME);
$this->setDescription(static::DESCRIPTION);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$postInstallTaskPlugins = $this->getFactory()->getPostInstallTaskPlugins();

$this->info(sprintf('Executing post install tasks (%s).', count($postInstallTaskPlugins)));

foreach ($postInstallTaskPlugins as $postInstallTaskPlugin) {
$postInstallTaskPlugin->run($input, $output);
}

$this->info('Post install tasks executed successfully.');

return static::CODE_SUCCESS;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

namespace Spryker\Zed\AppKernel\Communication\Plugin\AppKernel;

use Generated\Shared\Transfer\AppConfigCriteriaTransfer;
use Spryker\Zed\AppKernelExtension\Dependency\Plugin\PostInstallTaskPluginInterface;
use Spryker\Zed\Kernel\Communication\AbstractPlugin;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
* @method \Spryker\Zed\AppKernel\Business\AppKernelFacadeInterface getFacade()
* @method \Spryker\Zed\AppKernel\Persistence\AppKernelRepositoryInterface getRepository()
* @method \Spryker\Zed\AppKernel\AppKernelConfig getConfig()
* @method \Spryker\Zed\AppKernel\Communication\AppKernelCommunicationFactory getFactory()
*/
class TriggerAppConfigUpdatePostInstallTaskPlugin extends AbstractPlugin implements PostInstallTaskPluginInterface
stereomon marked this conversation as resolved.
Show resolved Hide resolved
{
/**
* {@inheritDoc}
* - Triggers AppConfig update for all connected tenants.
*
* @api
*/
public function run(InputInterface $input, OutputInterface $output): void
{
$tenantIdentifiers = $this->getRepository()->getConnectedTenantIdentifiers();

$output->writeln(sprintf('Triggering AppConfig update for all connected tenants (%s).', count($tenantIdentifiers)));

foreach ($tenantIdentifiers as $tenantIdentifier) {
$this->triggerAppConfigUpdate($tenantIdentifier);
}
}

protected function triggerAppConfigUpdate(string $tenantIdentifier): void
{
$appConfigCriteriaTransfer = (new AppConfigCriteriaTransfer())
->setTenantIdentifier($tenantIdentifier)
->setIsActive(true);
stereomon marked this conversation as resolved.
Show resolved Hide resolved

$appConfigTransfer = $this->getFacade()->getConfig($appConfigCriteriaTransfer);
$this->getFacade()->saveConfig($appConfigTransfer);
}
}
Loading
Loading