Skip to content

Commit 659354c

Browse files
committed
feat(bridge): APIP bridge started
1 parent abea3da commit 659354c

23 files changed

+904
-26
lines changed

.github/workflows/daily.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ jobs:
8181

8282
# —— Infection —————————————————————————————————————————————————————
8383
- name: Infection
84-
run: php vendor/bin/infection --no-progress --min-covered-msi=85 --min-msi=80 --show-mutations --threads=4
84+
run: php vendor/bin/infection --no-progress --min-covered-msi=86 --min-msi=80 --show-mutations --threads=4
8585
env:
8686
SCHEDULER_REDIS_DSN: redis://127.0.0.1:6379/_symfony_scheduler_tasks
8787
INFECTION_BADGE_API_KEY: ${{ secrets.INFECTION_BADGE_API_KEY }}

.github/workflows/infection.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
composer-options: "--prefer-stable"
4242

4343
- name: Run Infection
44-
run: php vendor/bin/infection --no-progress --min-covered-msi=85 --min-msi=80 --show-mutations --threads=4
44+
run: php vendor/bin/infection --no-progress --min-covered-msi=86 --min-msi=80 --show-mutations --threads=4
4545
env:
4646
SCHEDULER_REDIS_DSN: redis://127.0.0.1:6379/_symfony_scheduler_tasks
4747
INFECTION_BADGE_API_KEY: ${{ secrets.INFECTION_BADGE_API_KEY }}

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ CHANGELOG
1010
* `scheduler:yield` command added (see https://github.com/Guikingone/SchedulerBundle/pull/19)
1111
* Documentation improvements (see https://github.com/Guikingone/SchedulerBundle/pull/19)
1212
* "Fluent" expressions (see https://github.com/Guikingone/SchedulerBundle/pull/26)
13+
* Api-Platform support (see https://github.com/Guikingone/SchedulerBundle/pull/25)
1314

1415
0.2.0
1516
-----

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- Retry / Remove / Sort policies
1919
- Background worker
2020
- [Symfony/Messenger](https://symfony.com/doc/current/messenger.html) integration
21+
- [API-Platform](https://api-platform.com/) support
2122

2223
## Installation
2324

@@ -77,6 +78,7 @@ When a task is configured, time to execute it, two approaches can be used:
7778

7879
* [Usage](doc/usage.md)
7980
* [Best practices](doc/best_practices.md)
81+
* [API-Platform](doc/api_platform.md)
8082
* [Tasks](doc/tasks.md)
8183
* [Transports](doc/transport.md)
8284
* [Commands](doc/command.md)

composer.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
"psr-4": {
1616
"SchedulerBundle\\": "src/",
1717
"SchedulerBundle\\Bridge\\": "src/Bridge/",
18+
"SchedulerBundle\\Bridge\\ApiPlatform\\": "src/Bridge/ApiPlatform/",
19+
"SchedulerBundle\\Bridge\\ApiPlatform\\Filter\\": "src/Bridge/ApiPlatform/Filter/",
1820
"SchedulerBundle\\Bridge\\Doctrine\\": "src/Bridge/Doctrine/",
1921
"SchedulerBundle\\Bridge\\Doctrine\\SchemaListener\\": "src/Bridge/Doctrine/SchemaListener/",
2022
"SchedulerBundle\\Bridge\\Doctrine\\Transport\\": "src/Bridge/Doctrine/Transport/",
@@ -46,6 +48,8 @@
4648
"psr-4": {
4749
"Tests\\SchedulerBundle\\": "tests/",
4850
"Tests\\SchedulerBundle\\Bridge\\": "tests/Bridge/",
51+
"Tests\\SchedulerBundle\\Bridge\\ApiPlatform\\": "tests/Bridge/ApiPlatform/",
52+
"Tests\\SchedulerBundle\\Bridge\\ApiPlatform\\Filter\\": "tests/Bridge/ApiPlatform/Filter/",
4953
"Tests\\SchedulerBundle\\Bridge\\Doctrine\\": "tests/Bridge/Doctrine/",
5054
"Tests\\SchedulerBundle\\Bridge\\Doctrine\\SchemaListener\\": "tests/Bridge/Doctrine/SchemaListener/",
5155
"Tests\\SchedulerBundle\\Bridge\\Doctrine\\Transport\\": "tests/Bridge/Doctrine/Transport/",

doc/api_platform.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# API-Platform
2+
3+
This bundle provides a bridge with API-Platform,

psalm.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@
1414
</projectFiles>
1515
<plugins>
1616
<pluginClass class="Psalm\PhpUnitPlugin\Plugin"/>
17-
<pluginClass class="Psalm\SymfonyPsalmPlugin\Plugin"/></plugins>
17+
<pluginClass class="Psalm\SymfonyPsalmPlugin\Plugin"/>
18+
</plugins>
1819
</psalm>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SchedulerBundle\Bridge\ApiPlatform;
6+
7+
use ApiPlatform\Core\DataProvider\ContextAwareCollectionDataProviderInterface;
8+
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
9+
use Psr\Log\LoggerInterface;
10+
use Psr\Log\NullLogger;
11+
use SchedulerBundle\Bridge\ApiPlatform\Filter\SearchFilter;
12+
use SchedulerBundle\Task\TaskInterface;
13+
use SchedulerBundle\Task\TaskListInterface;
14+
use SchedulerBundle\Transport\TransportInterface;
15+
use Throwable;
16+
use function array_key_exists;
17+
18+
/**
19+
* @author Guillaume Loulier <[email protected]>
20+
*/
21+
final class CollectionDataProvider implements ContextAwareCollectionDataProviderInterface, RestrictedDataProviderInterface
22+
{
23+
private SearchFilter $searchFilter;
24+
private TransportInterface $transport;
25+
private LoggerInterface $logger;
26+
27+
public function __construct(
28+
SearchFilter $searchFilter,
29+
TransportInterface $transport,
30+
?LoggerInterface $logger = null
31+
) {
32+
$this->searchFilter = $searchFilter;
33+
$this->transport = $transport;
34+
$this->logger = $logger ?: new NullLogger();
35+
}
36+
37+
/**
38+
* {@inheritdoc}
39+
*/
40+
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
41+
{
42+
return $resourceClass === TaskInterface::class;
43+
}
44+
45+
/**
46+
* {@inheritdoc}
47+
*/
48+
public function getCollection(string $resourceClass, string $operationName = null, array $context = []): TaskListInterface
49+
{
50+
try {
51+
$list = $this->transport->list();
52+
} catch (Throwable $throwable) {
53+
$this->logger->critical('The list cannot be retrieved', [
54+
'error' => $throwable->getMessage(),
55+
]);
56+
57+
throw $throwable;
58+
}
59+
60+
if (array_key_exists('filters', $context) && [] !== $context['filters']) {
61+
return $this->searchFilter->filter($list, $context['filters']);
62+
}
63+
64+
return $list;
65+
}
66+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SchedulerBundle\Bridge\ApiPlatform\Filter;
6+
7+
use ApiPlatform\Core\Api\FilterInterface;
8+
use SchedulerBundle\Task\TaskInterface;
9+
use SchedulerBundle\Task\TaskListInterface;
10+
use function get_class;
11+
12+
/**
13+
* @author Guillaume Loulier <[email protected]>
14+
*/
15+
final class SearchFilter implements FilterInterface
16+
{
17+
/**
18+
* {@inheritdoc}
19+
*/
20+
public function getDescription(string $resourceClass): array
21+
{
22+
if (TaskInterface::class !== $resourceClass) {
23+
return [];
24+
}
25+
26+
return [
27+
'expression' => [
28+
'type' => 'string',
29+
'required' => false,
30+
'property' => 'expression',
31+
'swagger' => [
32+
'description' => 'Filter tasks using the expression',
33+
'name' => 'expression',
34+
'type' => 'string',
35+
],
36+
],
37+
'queued' => [
38+
'type' => 'bool',
39+
'required' => false,
40+
'property' => 'queued',
41+
'swagger' => [
42+
'description' => 'Filter tasks that are queued',
43+
'name' => 'queued',
44+
'type' => 'bool',
45+
],
46+
],
47+
'state' => [
48+
'type' => 'string',
49+
'required' => false,
50+
'property' => 'state',
51+
'swagger' => [
52+
'description' => 'Filter tasks with a specific state',
53+
'name' => 'state',
54+
'type' => 'string',
55+
],
56+
],
57+
'timezone' => [
58+
'type' => 'string',
59+
'required' => false,
60+
'property' => 'timezone',
61+
'swagger' => [
62+
'description' => 'Filter tasks scheduled using a specific timezone',
63+
'name' => 'timezone',
64+
'type' => 'string',
65+
],
66+
],
67+
'type' => [
68+
'type' => 'string',
69+
'required' => false,
70+
'property' => 'type',
71+
'swagger' => [
72+
'description' => 'Filter tasks depending on internal type',
73+
'name' => 'timezone',
74+
'type' => 'string',
75+
],
76+
],
77+
];
78+
}
79+
80+
public function filter(TaskListInterface $list, array $filters = []): TaskListInterface
81+
{
82+
if ([] === $filters) {
83+
return $list;
84+
}
85+
86+
if (0 === $list->count()) {
87+
return $list;
88+
}
89+
90+
foreach ($filters as $filter => $value) {
91+
switch ($filter) {
92+
case 'expression':
93+
$list = $list->filter(fn (TaskInterface $task): bool => $value === $task->getExpression());
94+
break;
95+
case 'queued':
96+
$list = $list->filter(fn (TaskInterface $task): bool => $task->isQueued());
97+
break;
98+
case 'state':
99+
$list = $list->filter(fn (TaskInterface $task): bool => $value === $task->getState());
100+
break;
101+
case 'timezone':
102+
$list = $list->filter(fn (TaskInterface $task): bool => null !== $task->getTimezone() && $value === $task->getTimezone()->getName());
103+
break;
104+
case 'type':
105+
$list = $list->filter(fn (TaskInterface $task): bool => $value === get_class($task));
106+
break;
107+
}
108+
}
109+
110+
return $list;
111+
}
112+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SchedulerBundle\Bridge\ApiPlatform;
6+
7+
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
8+
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
9+
use Psr\Log\LoggerInterface;
10+
use Psr\Log\NullLogger;
11+
use SchedulerBundle\Task\TaskInterface;
12+
use SchedulerBundle\Transport\TransportInterface;
13+
use Throwable;
14+
use function sprintf;
15+
16+
/**
17+
* @author Guillaume Loulier <[email protected]>
18+
*/
19+
final class ItemDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface
20+
{
21+
private TransportInterface $transport;
22+
private LoggerInterface $logger;
23+
24+
public function __construct(
25+
TransportInterface $transport,
26+
?LoggerInterface $logger = null
27+
) {
28+
$this->transport = $transport;
29+
$this->logger = $logger ?: new NullLogger();
30+
}
31+
32+
/**
33+
* {@inheritdoc}
34+
*/
35+
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
36+
{
37+
return $resourceClass === TaskInterface::class;
38+
}
39+
40+
/**
41+
* {@inheritdoc}
42+
*/
43+
public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): TaskInterface
44+
{
45+
try {
46+
$task = $this->transport->get($id);
47+
} catch (Throwable $throwable) {
48+
$this->logger->critical(sprintf('The task "%s" cannot be found', $id), [
49+
'error' => $throwable->getMessage(),
50+
]);
51+
52+
throw $throwable;
53+
}
54+
55+
return $task;
56+
}
57+
}

0 commit comments

Comments
 (0)