Skip to content

Commit 74cc3e3

Browse files
yann-eugoneJ-Ben87
andauthored
Add user interface to view executed jobs (#104)
* Add user interface to view executed jobs * Allow symfony/css-selector ^5.0 * Fixed potentially non existing assert method * Fixed missing array structure declaration in ConfigurableTemplating * Fixed code style * Fixed some details on bootstrap4 rendering * Add screenshots of UI * Allow to access child jobs on their own * Pluralize job routing * Add screenshot of warnings * Fixed tests * Screenshots gallery * Screenshots gallery * Move sonata screenshot gallery * Added HTTP method requirements to routes * Fixed typo in Yokai * Fixed breadcrumb building errors * Fixed wrong implementation of TemplatingInterface in documentation * Apply doc typo suggestions from code review Co-authored-by: Benoit Jouhaud <[email protected]> * Add test for not finding child job * Inline NotFoundHttpException * Fixed response statuses priorities --------- Co-authored-by: Yann Eugoné <[email protected]> Co-authored-by: Benoit Jouhaud <[email protected]>
1 parent 07a8566 commit 74cc3e3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+3526
-8
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ composer require yokai/batch-symfony-framework
2525
This package provides:
2626

2727
- [integration](docs/getting-started.md) with Symfony framework
28+
- a [UI](docs/ui.md) with Symfony framework
2829

2930

3031
## Contribution

composer.json

+13
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,21 @@
2525
}
2626
},
2727
"require-dev": {
28+
"sonata-project/admin-bundle": "^4.0",
29+
"symfony/filesystem": "^5.0|^6.0",
30+
"symfony/form": "^5.0|^6.0",
31+
"symfony/security-bundle": "^5.0|^6.0",
32+
"symfony/translation": "^5.0|^6.0",
33+
"symfony/twig-bundle": "^5.0|^6.0",
2834
"phpunit/phpunit": "^9.5"
2935
},
36+
"suggest": {
37+
"sonata-project/admin-bundle": "If you want a SonataAdmin like rendering in the user interface",
38+
"symfony/form": "If you want the JobExecution form filter in the user interface",
39+
"symfony/security-bundle": "If you want to secure the access to JobExecution in the user interface",
40+
"symfony/translation": "Required if you want to enable the user interface",
41+
"symfony/twig-bundle": "Required if you want to enable the user interface"
42+
},
3043
"autoload-dev": {
3144
"psr-4": {
3245
"Yokai\\Batch\\Tests\\Bridge\\Symfony\\Framework\\": "tests/"

docs/images/bootstrap4-children.png

44.7 KB
Loading

docs/images/bootstrap4-details.png

46.9 KB
Loading

docs/images/bootstrap4-list.png

69.5 KB
Loading

docs/images/bootstrap4-warnings.png

40.5 KB
Loading

docs/images/sonata-children.png

51.5 KB
Loading

docs/images/sonata-details.png

57.8 KB
Loading

docs/images/sonata-list.png

72.5 KB
Loading

docs/images/sonata-warnings.png

44.8 KB
Loading

docs/ui.md

+196
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# User Interface
2+
3+
The package is shipped with few routes that will allow you and your users, to watch for `JobExecution`.
4+
5+
<img src="images/bootstrap4-list.png" alt="Bootstrap 4 - List action" width="23%"> <img src="images/bootstrap4-details.png" alt="Bootstrap 4 - Detail : Information" width="23%"> <img src="images/bootstrap4-children.png" alt="Bootstrap 4 - Detail : Children" width="23%"> <img src="images/bootstrap4-warnings.png" alt="Bootstrap 4 - Detail : Warnings" width="23%">
6+
7+
8+
## Installation
9+
10+
For the UI to be enabled, it is required that you install some dependencies:
11+
```shell
12+
composer require symfony/translation symfony/twig-bundle
13+
```
14+
15+
16+
## Configuration
17+
18+
The UI is disabled by default, you must enable it explicitely:
19+
```yaml
20+
# config/packages/yokai_batch.yaml
21+
yokai_batch:
22+
ui:
23+
enabled: true
24+
```
25+
26+
You will also need to import bundle routes:
27+
```yaml
28+
# config/routes/yokai_batch.yaml
29+
_yokai_batch:
30+
resource: "@YokaiBatchBundle/Resources/routing/ui.xml"
31+
```
32+
33+
### Templating
34+
35+
The templating service is used by the [JobController](../src/UserInterface/Controller/JobController.php) to render its templates.
36+
It's a wrapper around [Twig](https://twig.symfony.com/), for you to control templates used, and variables passed.
37+
38+
> By default
39+
> - the templating will find templates like `@YokaiBatch/bootstrap4/*.html.twig`
40+
> - the template base view will be `base.html.twig`
41+
42+
You can configure a prefix for all templates:
43+
```yaml
44+
# config/packages/yokai_batch.yaml
45+
yokai_batch:
46+
ui:
47+
templating:
48+
prefix: 'batch/job/'
49+
```
50+
> With this configuration, we will look for templates like `batch/job/*.html.twig`.
51+
52+
You can also configure the name of the base template for the root views of that bundle:
53+
```yaml
54+
# config/packages/yokai_batch.yaml
55+
yokai_batch:
56+
ui:
57+
templating:
58+
base_template: 'layout.html.twig'
59+
```
60+
> With this configuration, the template base view will be `layout.html.twig`.
61+
62+
If these are not enough, or if you need to add more variables to context, you can configure a service:
63+
```yaml
64+
# config/packages/yokai_batch.yaml
65+
yokai_batch:
66+
ui:
67+
templating:
68+
service: 'App\Batch\AppTemplating'
69+
```
70+
71+
And create the class that will cover the templating:
72+
```php
73+
<?php
74+
75+
declare(strict_types=1);
76+
77+
namespace App\Batch;
78+
79+
use Yokai\Batch\Bridge\Symfony\Framework\UserInterface\Templating\TemplatingInterface;
80+
81+
final class AppTemplating implements TemplatingInterface
82+
{
83+
public function name(string $name): string
84+
{
85+
return "another-$name"; // change $name if you want
86+
}
87+
88+
public function context(array $context): array;
89+
{
90+
return \array_merge($context, ['foo' => 'bar']); // add variables to $context if you want
91+
}
92+
}
93+
```
94+
95+
> **Note** You can also use the `Yokai\Batch\Bridge\Symfony\Framework\UserInterface\Templating\ConfigurableTemplating` that will cover both prefix and static variables at construction.
96+
97+
98+
### Filtering
99+
100+
The `JobExecution` list includes a filter form, but you will need another optional dependency:
101+
```shell
102+
composer require symfony/form
103+
```
104+
105+
### Security
106+
107+
There is no access control over `JobExecution` by default, you will need another optional dependency:
108+
```shell
109+
composer require symfony/security-bundle
110+
```
111+
112+
Every security attribute the bundle is using is configurable:
113+
```yaml
114+
# config/packages/yokai_batch.yaml
115+
yokai_batch:
116+
ui:
117+
security:
118+
attributes:
119+
list: ROLE_JOB_LIST # defaults to IS_AUTHENTICATED
120+
view: ROLE_JOB_VIEW # defaults to IS_AUTHENTICATED
121+
traces: ROLE_JOB_TRACES # defaults to IS_AUTHENTICATED
122+
logs: ROLE_JOB_LOGS # defaults to IS_AUTHENTICATED
123+
```
124+
125+
Optionally, you can register a voter for these attributes.
126+
This is especially useful if you need different access control rules per `JobExecution`.
127+
```php
128+
<?php
129+
130+
declare(strict_types=1);
131+
132+
namespace App\Security;
133+
134+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
135+
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
136+
use Yokai\Batch\JobExecution;
137+
138+
final class JobVoter extends Voter
139+
{
140+
protected function supports(string $attribute, mixed $subject): bool
141+
{
142+
return \str_starts_with($attribute, 'JOB_');
143+
}
144+
145+
/**
146+
* @param JobExecution|null $subject
147+
*/
148+
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
149+
{
150+
// TODO: Implement voteOnAttribute() method.
151+
}
152+
}
153+
```
154+
155+
156+
## Integration with SonataAdminBundle
157+
158+
If you are on a [SonataAdmin](https://symfony.com/bundles/SonataAdminBundle/current/index.html) project.
159+
The bundle got you covered with a dedicated templating services and templates.
160+
161+
<img src="images/sonata-list.png" alt="Sonata - List action" width="23%"> <img src="images/sonata-details.png" alt="Sonata - Detail : Information" width="23%"> <img src="images/sonata-children.png" alt="Sonata - Detail : Children" width="23%"> <img src="images/sonata-warnings.png" alt="Sonata - Detail : Warnings" width="23%">
162+
163+
```shell
164+
composer require sonata-project/admin-bundle
165+
```
166+
167+
```yaml
168+
# config/packages/yokai_batch.yaml
169+
yokai_batch:
170+
ui:
171+
templating: sonata
172+
```
173+
> With this configuration, we will look for templates like `@YokaiBatch/sonata/*.html.twig`.
174+
175+
176+
## Customizing templates
177+
178+
You can override templates like [described it Symfony's documentation](https://symfony.com/doc/current/bundles/override.html).
179+
Examples:
180+
- `templates/bundles/YokaiBatchBundle/bootstrap4/list.html.twig`
181+
- `templates/bundles/YokaiBatchBundle/bootstrap4/show/_parameters.html.twig`
182+
183+
But you can also register job name dedicated templates if you need some specific view for one of your jobs:
184+
- `templates/bundles/YokaiBatchBundle/bootstrap4/show/{job name}/_children-executions.html.twig`
185+
- `templates/bundles/YokaiBatchBundle/bootstrap4/show/{job name}/_failures.html.twig`
186+
- `templates/bundles/YokaiBatchBundle/bootstrap4/show/{job name}/_general.html.twig`
187+
- `templates/bundles/YokaiBatchBundle/bootstrap4/show/{job name}/_information.html.twig`
188+
- `templates/bundles/YokaiBatchBundle/bootstrap4/show/{job name}/_parameters.html.twig`
189+
- `templates/bundles/YokaiBatchBundle/bootstrap4/show/{job name}/_summary.html.twig`
190+
- `templates/bundles/YokaiBatchBundle/bootstrap4/show/{job name}/_warnings.html.twig`
191+
192+
## On the same subject
193+
194+
- [What is a job execution storage ?](https://github.com/yokai-php/batch/blob/0.x/docs/domain/job-execution-storage.md)
195+
- [What is a job ?](https://github.com/yokai-php/batch/blob/0.x/docs/domain/job.md)
196+
- [What is a job launcher ?](https://github.com/yokai-php/batch/blob/0.x/docs/domain/job-launcher.md)

src/DependencyInjection/CompilerPass/RegisterJobsCompilerPass.php

+5
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ public function process(ContainerBuilder $container): void
3030

3131
$container->getDefinition('yokai_batch.job_registry')
3232
->setArgument('$jobs', ServiceLocatorTagPass::register($container, $jobs));
33+
34+
if ($container->hasDefinition('yokai_batch.ui.filter_form')) {
35+
$container->getDefinition('yokai_batch.ui.filter_form')
36+
->setArgument('$jobs', \array_keys($jobs));
37+
}
3338
}
3439

3540
/**

src/DependencyInjection/Configuration.php

+103
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,38 @@
1010

1111
/**
1212
* Configuration for yokai/batch Symfony Bundle.
13+
*
14+
* @phpstan-type Config array{
15+
* storage: StorageConfig,
16+
* ui: UserInterfaceConfig,
17+
* }
18+
* @phpstan-type StorageConfig array{
19+
* service?: string,
20+
* dbal?: array{
21+
* connection: string,
22+
* table: string,
23+
* },
24+
* filesystem: array{
25+
* serializer: string,
26+
* dir: string,
27+
* },
28+
* }
29+
* @phpstan-type UserInterfaceConfig array{
30+
* enabled: bool,
31+
* security: array{
32+
* attributes: array{
33+
* list: string,
34+
* view: string,
35+
* traces: string,
36+
* logs: string,
37+
* },
38+
* },
39+
* templating: array{
40+
* prefix: string|null,
41+
* service: string|null,
42+
* base_template: string|null,
43+
* },
44+
* }
1345
*/
1446
final class Configuration implements ConfigurationInterface
1547
{
@@ -21,6 +53,7 @@ public function getConfigTreeBuilder(): TreeBuilder
2153
$root
2254
->children()
2355
->append($this->storage())
56+
->append($this->ui())
2457
->end()
2558
;
2659

@@ -63,4 +96,74 @@ private function storage(): ArrayNodeDefinition
6396

6497
return $node;
6598
}
99+
100+
private function ui(): ArrayNodeDefinition
101+
{
102+
/** @var ArrayNodeDefinition $node */
103+
$node = (new TreeBuilder('ui'))->getRootNode();
104+
105+
$node
106+
->addDefaultsIfNotSet()
107+
->canBeEnabled()
108+
->children()
109+
->arrayNode('templating')
110+
->addDefaultsIfNotSet()
111+
->beforeNormalization()
112+
->always(function (string|array $value) {
113+
if (is_string($value)) {
114+
$value = match ($value) {
115+
'bootstrap4' => ['prefix' => '@YokaiBatch/bootstrap4', 'service' => null],
116+
'sonata' => ['service' => 'yokai_batch.ui.sonata_templating', 'prefix' => null],
117+
default => throw new \InvalidArgumentException(
118+
\sprintf('Unknown templating shortcut "%s".', $value),
119+
),
120+
};
121+
}
122+
123+
if (!isset($value['service']) && !isset($value['prefix'])) {
124+
throw new \InvalidArgumentException(
125+
'You must either configure "service" or "prefix".',
126+
);
127+
} elseif (isset($value['service']) && isset($value['prefix'])) {
128+
throw new \InvalidArgumentException(
129+
'You cannot configure "service" and "prefix" at the same time.',
130+
);
131+
}
132+
133+
return $value;
134+
})
135+
->end()
136+
->children()
137+
->scalarNode('service')->defaultNull()->end()
138+
->scalarNode('prefix')->defaultValue('@YokaiBatch/bootstrap4')->end()
139+
->scalarNode('base_template')->defaultValue('base.html.twig')->end()
140+
->end()
141+
->end()
142+
->arrayNode('security')
143+
->addDefaultsIfNotSet()
144+
->children()
145+
->arrayNode('attributes')
146+
->addDefaultsIfNotSet()
147+
->children()
148+
->scalarNode('list')
149+
->defaultValue('IS_AUTHENTICATED')
150+
->end()
151+
->scalarNode('view')
152+
->defaultValue('IS_AUTHENTICATED')
153+
->end()
154+
->scalarNode('traces')
155+
->defaultValue('IS_AUTHENTICATED')
156+
->end()
157+
->scalarNode('logs')
158+
->defaultValue('IS_AUTHENTICATED')
159+
->end()
160+
->end()
161+
->end()
162+
->end()
163+
->end()
164+
->end()
165+
;
166+
167+
return $node;
168+
}
66169
}

0 commit comments

Comments
 (0)