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

13 lauch process without context or input #19

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e8a4c26
#13 ability to add modal confirmation or nothing instead of launch fr…
clever-age-gtonon Dec 12, 2024
85f3145
#13 ability to add modal confirmation or nothing instead of launch fr…
clever-age-gtonon Dec 16, 2024
c9df400
#13 ability to add modal confirmation or nothing instead of launch fr…
clever-age-gtonon Dec 16, 2024
1ee039b
#14 Quality. Phpstan remove - '#type has no value type specif…
clever-age-gtonon Dec 13, 2024
ccce788
#14 Quality. Phpstan remove missingType.generics
clever-age-gtonon Dec 13, 2024
1173eee
#14 Quality. Phpstan 7
clever-age-gtonon Dec 13, 2024
fecf2a2
#14 Quality. Phpstan 8
clever-age-gtonon Dec 13, 2024
85cf872
#14 Quality. PhpCs
clever-age-gtonon Dec 13, 2024
e996d47
#14 Quality. Phpstan use phpstan type to prevent duplicate definitions.
clever-age-gtonon Dec 16, 2024
1911fbf
#14 Quality. Phpstan use phpstan type to prevent duplicate definitions.
clever-age-gtonon Dec 16, 2024
b1433dc
#13 ability to add modal confirmation or nothing instead of launch fr…
clever-age-gtonon Dec 12, 2024
65b2003
#13 ability to add modal confirmation or nothing instead of launch fr…
clever-age-gtonon Dec 16, 2024
5507185
Merge branch 'prepare-release' into 13_lauch_process_without_context_…
tonongregory Dec 16, 2024
ad6d10a
#13 ability to add modal confirmation or nothing instead of launch fr…
clever-age-gtonon Dec 16, 2024
dd2e099
#13 ability to add modal confirmation or nothing instead of launch fr…
clever-age-gtonon Dec 16, 2024
b103677
#13 ability to add modal confirmation or nothing instead of launch fr…
clever-age-gtonon Dec 17, 2024
05c1878
#13 ability to add modal confirmation or nothing instead of launch fr…
clever-age-gtonon Dec 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions config/services/twig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ services:
tags:
- { name: 'twig.extension' }

cleverage_ui_process.twig.process_extension:
class: CleverAge\UiProcessBundle\Twig\Extension\ProcessExtension
public: false
tags:
- { name: 'twig.extension' }

cleverage_ui_process.twig.log_level_extension_runtime:
class: CleverAge\UiProcessBundle\Twig\Runtime\LogLevelExtensionRuntime
public: false
Expand All @@ -37,3 +43,17 @@ services:
arguments:
- '@cleverage_ui_process.repository.process_execution'
- '@cleverage_ui_process.manager.process_configuration'

cleverage_ui_process.twig.process_extension_runtime:
class: CleverAge\UiProcessBundle\Twig\Runtime\ProcessExtensionRuntime
public: false
tags:
- { name: 'twig.runtime' }
arguments:
- '@cleverage_ui_process.manager.process_configuration'

cleverage_ui_process.twig.component.bootstrap_modal:
class: CleverAge\UiProcessBundle\Twig\Components\BootstrapModal
shared: false
tags:
- { name: 'twig.component', key: 'ui:BootstrapModal', template: '@CleverAgeUiProcess/components/BootstrapModal.html.twig' }
10 changes: 10 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ Now you can access UI Process via http://your-domain.com/process

## Features

### Launch process via UI
From UI "Process List" menu entry you can run a process by clicking on "Rocket" action.
You can manage this behaviour by setting some ui option `ui_launch_mode`

| Value | UI behaviour |
|:---------------:|:--------------------------------------------------------------:|
| modal (default) | On click, open confirmation modal to confirm process execution |
| form | On click, open a form to set input and context execution |
| null | Run process without any confirmation |

### Launch process via http request
You can launch a process via http post request
First you need to generate a token via UI User edit form. The UiProcess generate for you a auth token (keep it in secured area, it will display once).
Expand Down
55 changes: 43 additions & 12 deletions src/Controller/Admin/Process/LaunchAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace CleverAge\UiProcessBundle\Controller\Admin\Process;

use CleverAge\ProcessBundle\Exception\MissingProcessException;
use CleverAge\UiProcessBundle\Entity\User;
use CleverAge\UiProcessBundle\Form\Type\LaunchType;
use CleverAge\UiProcessBundle\Manager\ProcessConfigurationsManager;
Expand All @@ -39,24 +40,43 @@
#[IsGranted('ROLE_USER')]
class LaunchAction extends AbstractController
{
public function __construct(private readonly MessageBusInterface $messageBus)
{
}

public function __invoke(
RequestStack $requestStack,
MessageBusInterface $messageBus,
string $uploadDirectory,
ProcessConfigurationsManager $processConfigurationsManager,
AdminContext $context,
): Response {
$uiOptions = $processConfigurationsManager->getUiOptions($requestStack->getMainRequest()?->get('process') ?? '');
$processCode = $requestStack->getMainRequest()?->get('process');
if (null === $processCode) {
throw new MissingProcessException();
}
$uiOptions = $processConfigurationsManager->getUiOptions($processCode);
if (null === $uiOptions) {
throw new \InvalidArgumentException('Missing UI Options');
}
if (null === $uiOptions['ui_launch_mode'] || 'modal' === $uiOptions['ui_launch_mode']) {
$this->dispatch($processCode);
$this->addFlash(
'success',
'Process has been added to queue. It will start as soon as possible'
);

return $this->redirectToRoute('process', ['routeName' => 'process_list']);
}
$form = $this->createForm(
LaunchType::class,
null,
[
'constraints' => $uiOptions['constraints'] ?? [],
'process_code' => $requestStack->getMainRequest()?->get('process'),
'constraints' => $uiOptions['constraints'],
'process_code' => $processCode,
]
);
if (false === $form->isSubmitted()) {
$default = $uiOptions['default'] ?? [];
$default = $uiOptions['default'];
if (false === $form->get('input')->getConfig()->getType()->getInnerType() instanceof TextType
&& isset($default['input'])
) {
Expand All @@ -72,16 +92,11 @@ public function __invoke(
(new Filesystem())->dumpFile($filename, $input->getContent());
$input = $filename;
}

$message = new ProcessExecuteMessage(
$this->dispatch(
$form->getConfig()->getOption('process_code'),
$input,
array_merge(
['execution_user' => $this->getUser()?->getEmail()],
$form->get('context')->getData()
)
$form->get('context')->getData()
);
$messageBus->dispatch($message);
$this->addFlash(
'success',
'Process has been added to queue. It will start as soon as possible'
Expand All @@ -99,6 +114,22 @@ public function __invoke(
);
}

/**
* @param mixed[] $context
*/
protected function dispatch(string $processCode, mixed $input = null, array $context = []): void
{
$message = new ProcessExecuteMessage(
$processCode,
$input,
array_merge(
['execution_user' => $this->getUser()?->getEmail()],
$context
)
);
$this->messageBus->dispatch($message);
}

protected function getUser(): ?User
{
/** @var User $user */
Expand Down
4 changes: 4 additions & 0 deletions src/Manager/ProcessConfigurationsManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
* @phpstan-type UiOptions array{
* 'source': ?string,
* 'target': ?string,
* 'ui_launch_mode': ?string,
* 'run_confirmation_modal': bool,
* 'entrypoint_type': string,
* 'constraints': Constraint[],
* 'run': 'null|bool',
Expand Down Expand Up @@ -76,6 +78,7 @@ private function resolveUiOptions(array $options): array
'source' => null,
'target' => null,
'entrypoint_type' => 'text',
'ui_launch_mode' => 'modal',
'constraints' => [],
'run' => null,
'default' => function (OptionsResolver $defaultResolver) {
Expand All @@ -95,6 +98,7 @@ private function resolveUiOptions(array $options): array
);
$uiResolver->setAllowedValues('entrypoint_type', ['text', 'file']);
$uiResolver->setNormalizer('constraints', fn (Options $options, array $values): array => (new ConstraintLoader())->buildConstraints($values));
$uiResolver->setAllowedValues('ui_launch_mode', ['modal', null, 'form']);
});
/**
* @var array{'ui': UiOptions} $options
Expand Down
20 changes: 20 additions & 0 deletions src/Twig/Components/BootstrapModal.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

/*
* This file is part of the CleverAge/UiProcessBundle package.
*
* Copyright (c) Clever-Age
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace CleverAge\UiProcessBundle\Twig\Components;

class BootstrapModal
{
public ?string $id = null;
public ?string $title = null;
public ?string $message = null;
public ?string $confirmUrl = null;
}
31 changes: 31 additions & 0 deletions src/Twig/Extension/ProcessExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

/*
* This file is part of the CleverAge/UiProcessBundle package.
*
* Copyright (c) Clever-Age
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace CleverAge\UiProcessBundle\Twig\Extension;

use CleverAge\UiProcessBundle\Twig\Runtime\ProcessExtensionRuntime;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class ProcessExtension extends AbstractExtension
{
public function getFunctions(): array
{
return [
new TwigFunction(
'resolve_ui_options',
[ProcessExtensionRuntime::class, 'getUiOptions']
),
];
}
}
33 changes: 33 additions & 0 deletions src/Twig/Runtime/ProcessExtensionRuntime.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/*
* This file is part of the CleverAge/UiProcessBundle package.
*
* Copyright (c) Clever-Age
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace CleverAge\UiProcessBundle\Twig\Runtime;

use CleverAge\UiProcessBundle\Manager\ProcessConfigurationsManager;
use Twig\Extension\RuntimeExtensionInterface;

/**
* @phpstan-import-type UiOptions from ProcessConfigurationsManager
*/
class ProcessExtensionRuntime implements RuntimeExtensionInterface
{
public function __construct(protected ProcessConfigurationsManager $processConfigurationsManager)
{
}

/**
* @return UiOptions|array{}
*/
public function getUiOptions(string $code): array
{
return $this->processConfigurationsManager->getUiOptions($code) ?? [];
}
}
4 changes: 1 addition & 3 deletions templates/admin/field/report.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,5 @@
{# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #}
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
<ul>
{% for key, item in field.value %}
<li class="list-group-item"><span class="badge badge-{{ log_css_class(key) }}">{{ key }}</span> : {{ item }}</li>
{% endfor %}
{{ field.value }}
</ul>
19 changes: 16 additions & 3 deletions templates/admin/process/list.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
{# @var process \CleverAge\ProcessBundle\Configuration\ProcessConfiguration #}
{% for process in processes %}
{% set lastExecution = get_last_execution_date(process.code) %}
{% set uiOptions = resolve_ui_options(process.code) %}
{% set statusClass = '' %}
{% if lastExecution is not null %}
{% set statusClass = lastExecution.status.value == 'failed' ? 'danger' : 'success' %}
Expand All @@ -32,9 +33,15 @@
<td>{% if process.options.ui.source is defined %}{{ process.options.ui.source }}{% endif %}</td>
<td>{% if process.options.ui.target is defined %}{{ process.options.ui.target }}{% endif %}</td>
<td class="text-center">
<a class="px-1" data-toggle="tooltip" data-placement="top" title="{{ 'Launch'|trans }}" href="{{ url('process', {routeName: 'process_launch', process: process.code}) }}">
<i class="fas fa-rocket"></i>
</a>
{% if ('modal' == uiOptions.ui_launch_mode) %}
<a class="px-1" data-toggle="tooltip" data-placement="top" title="{{ 'Launch'|trans }}" data-bs-toggle="modal" data-bs-target="#{{ process.code }}">
<i class="fas fa-rocket"></i>
</a>
{% else %}
<a class="px-1" data-toggle="tooltip" data-placement="top" title="{{ 'Launch'|trans }}" href="{{ url('process', {routeName: 'process_launch', process: process.code}) }}">
<i class="fas fa-rocket"></i>
</a>
{% endif %}
<a
class="px-1"
data-toggle="tooltip"
Expand All @@ -58,6 +65,12 @@
</a>
</td>
</tr>
<twig:ui:BootstrapModal
id="{{ process.code }}"
title="{{ 'Run process'|trans }} {{ process.code }}"
message="{{ 'Do you really want to run process %process% in background'|trans({'%process%': process.code}) }} ?"
confirmUrl="{{ url('process', {routeName: 'process_launch', process: process.code}) }}"
/>
{% endfor %}
{% endblock %}
</tbody>
Expand Down
37 changes: 37 additions & 0 deletions templates/components/BootstrapModal.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<div {{ attributes.defaults({
class: 'modal fade',
tabindex: '-1',
'aria-hidden': 'true',
id: id ? id : false,
}) }}
data-controller="bootstrap-modal"
>
<div class="modal-dialog">
<div class="modal-content">
{% block modal_full_content %}
{% if block('modal_header') %}
<div class="modal-header text-center">
{% block modal_header %}
<h5>{{ title }}</h5>
{% endblock %}
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
{% endif %}

<div class="modal-body">
{% block modal_body %}{{ message }}{% endblock %}
</div>

{% if block('modal_footer') %}
<div class="modal-footer">
{% block modal_footer %}
{% if null != confirmUrl %}
<a href="{{ confirmUrl }}"><button type="button" class="btn btn-primary">{{ 'Confirm'|trans }}</button></a>
{% endif %}
{% endblock %}
</div>
{% endif %}
{% endblock %}
</div>
</div>
</div>
Loading