gdbots/pbjx-bundle-php

View on GitHub
src/Command/PbjxCommand.php

Summary

Maintainability
B
4 hrs
Test Coverage
<?php
declare(strict_types=1);

namespace Gdbots\Bundle\PbjxBundle\Command;

use Gdbots\Bundle\PbjxBundle\Controller\PbjxController;
use Gdbots\Pbj\SchemaCurie;
use Gdbots\Schemas\Pbjx\Enum\Code;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;

#[AsCommand(name: 'pbjx')]
final class PbjxCommand extends Command
{
    use PbjxAwareCommandTrait;

    public function __construct(protected ContainerInterface $container)
    {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this
            ->setDescription('Handles pbjx messages (command, event, request) and returns an envelope with the result')
            ->setHelp(<<<EOF
The <info>%command.name%</info> command will create a single pbjx message using the json
payload provided and return an envelope (also json) with the results.

<info>php %command.full_name% 'gdbots:pbjx:request:echo-request' '{"msg":"hello"}'</info>

EOF
            )
            ->addOption(
                'user-agent',
                null,
                InputOption::VALUE_REQUIRED,
                'The http user agent to run as for this command.'
            )
            ->addOption(
                'in-memory',
                null,
                InputOption::VALUE_NONE,
                'Forces all transports to be "in_memory". Useful for debugging or ensuring sequential processing.'
            )
            ->addOption(
                'device-view',
                null,
                InputOption::VALUE_REQUIRED,
                'When gdbots/app-bundle is in use you can provide device-view to ' .
                'populate request and server attributes.'
            )
            ->addOption(
                'pretty',
                null,
                InputOption::VALUE_NONE,
                'Prints the json response with JSON_PRETTY_PRINT.'
            )
            ->addArgument(
                'curie',
                InputArgument::REQUIRED,
                'The pbj message curie to use for the provided payload (json).'
            )
            ->addArgument(
                'json',
                InputArgument::OPTIONAL,
                'The pbj message itself as json (on one line).'
            );
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $errOutput = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output;

        $curie = SchemaCurie::fromString($input->getArgument('curie'));
        $_SERVER['REQUEST_METHOD'] = 'POST';
        $_SERVER['CONTENT_TYPE'] = 'application/json';
        $_SERVER['HTTP_ACCEPT'] = 'application/json';
        $_SERVER['HTTP_ACCEPT_CHARSET'] = 'utf-8';
        $_SERVER['HTTP_USER_AGENT'] = $input->getOption('user-agent') ?: 'pbjx-console/2.x';

        $deviceView = $input->getOption('device-view');
        if (!empty($deviceView)) {
            $_SERVER['DEVICE_VIEW'] = $deviceView;
        }

        $request = Request::create(
            sprintf(
                '/pbjx/%s/%s/%s/%s',
                $curie->getVendor(),
                $curie->getPackage(),
                $curie->getCategory() ?: '_',
                $curie->getMessage()
            ),
            $_SERVER['REQUEST_METHOD'],
            [], // GET and POST (aka $_REQUEST)
            $_COOKIE,
            $_FILES,
            $_SERVER,
            $input->getArgument('json') ?: '{}'
        );

        /*
         * prepare the request object so http and console processing are virtually the same
         */
        $request->setRequestFormat('json');
        $request->attributes->set('pbjx_vendor', $curie->getVendor());
        $request->attributes->set('pbjx_package', $curie->getPackage());
        $request->attributes->set('pbjx_category', $curie->getCategory());
        $request->attributes->set('pbjx_message', $curie->getMessage());
        $request->attributes->set('pbjx_bind_unrestricted', true);
        $request->attributes->set('pbjx_console', true);
        if (!empty($deviceView)) {
            $request->attributes->set('device_view', $deviceView);
        }

        $this->useInMemoryTransports($input);
        $this->getRequestStack()->push($request);
        $envelope = $this->getPbjxController()->handleAction($request);

        try {
            $this->getPbjx()->triggerLifecycle($envelope, false);
        } catch (\Throwable $e) {
            /*
             * write to std error but return payload as is. Decorating the envelope
             * is not typically an exception worthy condition.
             */
            $errOutput->writeln('<error>' . $e->getMessage() . '</error>');
        }

        $envelope->set('ok', Code::OK->value === $envelope->get('code'));
        $output->writeln(json_encode($envelope, $input->getOption('pretty') ? JSON_PRETTY_PRINT : 0));

        return self::SUCCESS;
    }

    protected function getPbjxController(): PbjxController
    {
        return $this->container->get('gdbots_pbjx.pbjx_controller');
    }
}