• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 10508505187

22 Aug 2024 12:55PM UTC coverage: 7.704%. Remained the same
10508505187

push

github

soyuka
Merge 3.4

0 of 4 new or added lines in 4 files covered. (0.0%)

8840 existing lines in 284 files now uncovered.

12477 of 161949 relevant lines covered (7.7%)

22.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.49
/src/Metadata/Resource/Factory/OperationDefaultsTrait.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\Metadata\Resource\Factory;
15

16
use ApiPlatform\Metadata\ApiResource;
17
use ApiPlatform\Metadata\CollectionOperationInterface;
18
use ApiPlatform\Metadata\Delete;
19
use ApiPlatform\Metadata\Exception\RuntimeException;
20
use ApiPlatform\Metadata\Get;
21
use ApiPlatform\Metadata\GetCollection;
22
use ApiPlatform\Metadata\GraphQl\DeleteMutation;
23
use ApiPlatform\Metadata\GraphQl\Mutation;
24
use ApiPlatform\Metadata\GraphQl\Operation as GraphQlOperation;
25
use ApiPlatform\Metadata\GraphQl\Query;
26
use ApiPlatform\Metadata\GraphQl\QueryCollection;
27
use ApiPlatform\Metadata\GraphQl\Subscription;
28
use ApiPlatform\Metadata\HttpOperation;
29
use ApiPlatform\Metadata\Operation;
30
use ApiPlatform\Metadata\Operations;
31
use ApiPlatform\Metadata\Patch;
32
use ApiPlatform\Metadata\Post;
33
use ApiPlatform\Metadata\Put;
34
use ApiPlatform\Metadata\Util\CamelCaseToSnakeCaseNameConverter;
35
use ApiPlatform\State\CreateProvider;
36
use Psr\Log\LoggerInterface;
37

38
trait OperationDefaultsTrait
39
{
40
    private CamelCaseToSnakeCaseNameConverter $camelCaseToSnakeCaseNameConverter;
41
    private array $defaults = [];
42
    private LoggerInterface $logger;
43

44
    private function addGlobalDefaults(ApiResource|Operation $operation): ApiResource|Operation
45
    {
UNCOV
46
        $extraProperties = $this->defaults['extra_properties'] ?? [];
34✔
47

UNCOV
48
        foreach ($this->defaults as $key => $value) {
34✔
UNCOV
49
            if ('operations' === $key) {
34✔
50
                continue;
×
51
            }
52

UNCOV
53
            $upperKey = ucfirst($this->camelCaseToSnakeCaseNameConverter->denormalize($key));
34✔
UNCOV
54
            $getter = 'get'.$upperKey;
34✔
55

UNCOV
56
            if (!method_exists($operation, $getter)) {
34✔
UNCOV
57
                if (!isset($extraProperties[$key])) {
31✔
UNCOV
58
                    $extraProperties[$key] = $value;
31✔
59
                }
60

UNCOV
61
                continue;
31✔
62
            }
63

UNCOV
64
            $currentValue = $operation->{$getter}();
34✔
65

UNCOV
66
            if (\is_array($currentValue) && $currentValue) {
34✔
UNCOV
67
                $operation = $operation->{'with'.$upperKey}(array_merge($value, $currentValue));
34✔
68
            }
69

UNCOV
70
            if (null !== $currentValue) {
34✔
UNCOV
71
                continue;
34✔
72
            }
73

UNCOV
74
            $operation = $operation->{'with'.$upperKey}($value);
34✔
75
        }
76

UNCOV
77
        return $operation->withExtraProperties(array_merge($extraProperties, $operation->getExtraProperties()));
34✔
78
    }
79

80
    private function getResourceWithDefaults(string $resourceClass, string $shortName, ApiResource $resource): ApiResource
81
    {
UNCOV
82
        $resource = $resource
34✔
UNCOV
83
            ->withShortName($resource->getShortName() ?? $shortName)
34✔
UNCOV
84
            ->withClass($resourceClass);
34✔
85

UNCOV
86
        return $this->addGlobalDefaults($resource);
34✔
87
    }
88

89
    private function getDefaultHttpOperations($resource): iterable
90
    {
UNCOV
91
        if (enum_exists($resource->getClass())) {
23✔
UNCOV
92
            return new Operations([new GetCollection(paginationEnabled: false), new Get()]);
4✔
93
        }
94

UNCOV
95
        if (($defaultOperations = $this->defaults['operations'] ?? null) && null === $resource->getOperations()) {
23✔
96
            $operations = [];
×
97

98
            foreach ($defaultOperations as $defaultOperation) {
×
99
                $operations[] = new $defaultOperation();
×
100
            }
101

102
            return new Operations($operations);
×
103
        }
104

UNCOV
105
        $post = new Post();
23✔
UNCOV
106
        if ($resource->getUriTemplate() && !$resource->getProvider()) {
23✔
UNCOV
107
            $post = $post->withProvider(CreateProvider::class);
3✔
108
        }
109

UNCOV
110
        return [new Get(), new GetCollection(), $post, new Put(), new Patch(), new Delete()];
23✔
111
    }
112

113
    private function addDefaultGraphQlOperations(ApiResource $resource): ApiResource
114
    {
UNCOV
115
        $operations = enum_exists($resource->getClass()) ? [new QueryCollection(paginationEnabled: false), new Query()] : [new QueryCollection(), new Query(), (new Mutation())->withName('update'), (new DeleteMutation())->withName('delete'), (new Mutation())->withName('create')];
24✔
UNCOV
116
        $graphQlOperations = [];
24✔
UNCOV
117
        foreach ($operations as $operation) {
24✔
UNCOV
118
            [$key, $operation] = $this->getOperationWithDefaults($resource, $operation);
24✔
UNCOV
119
            $graphQlOperations[$key] = $operation;
24✔
120
        }
121

UNCOV
122
        if ($resource->getMercure()) {
24✔
UNCOV
123
            [$key, $operation] = $this->getOperationWithDefaults($resource, (new Subscription())->withDescription("Subscribes to the update event of a {$operation->getShortName()}."));
3✔
UNCOV
124
            $graphQlOperations[$key] = $operation;
3✔
125
        }
126

UNCOV
127
        return $resource->withGraphQlOperations($graphQlOperations);
24✔
128
    }
129

130
    /**
131
     * Adds nested query operations if there are no existing query ones on the resource.
132
     * They are needed when the resource is queried inside a root query, using a relation.
133
     * Since the nested argument is used, root queries will not be generated for these operations.
134
     */
135
    private function completeGraphQlOperations(ApiResource $resource): ApiResource
136
    {
UNCOV
137
        $graphQlOperations = $resource->getGraphQlOperations();
11✔
138

UNCOV
139
        $hasQueryOperation = false;
11✔
UNCOV
140
        $hasQueryCollectionOperation = false;
11✔
UNCOV
141
        foreach ($graphQlOperations as $operation) {
11✔
UNCOV
142
            if ($operation instanceof Query && !$operation instanceof QueryCollection) {
8✔
UNCOV
143
                $hasQueryOperation = true;
8✔
144
            }
UNCOV
145
            if ($operation instanceof QueryCollection) {
8✔
UNCOV
146
                $hasQueryCollectionOperation = true;
5✔
147
            }
148
        }
149

UNCOV
150
        if (!$hasQueryOperation) {
11✔
UNCOV
151
            $queryOperation = (new Query())->withNested(true);
6✔
UNCOV
152
            $graphQlOperations[$queryOperation->getName()] = $queryOperation;
6✔
153
        }
UNCOV
154
        if (!$hasQueryCollectionOperation) {
11✔
UNCOV
155
            $queryCollectionOperation = (new QueryCollection())->withNested(true);
10✔
UNCOV
156
            $graphQlOperations[$queryCollectionOperation->getName()] = $queryCollectionOperation;
10✔
157
        }
158

UNCOV
159
        return $resource->withGraphQlOperations($graphQlOperations);
11✔
160
    }
161

162
    private function getOperationWithDefaults(ApiResource $resource, Operation $operation, bool $generated = false, array $ignoredOptions = []): array
163
    {
164
        // Inherit from resource defaults
UNCOV
165
        foreach (get_class_methods($resource) as $methodName) {
34✔
UNCOV
166
            if (!str_starts_with($methodName, 'get')) {
34✔
UNCOV
167
                continue;
34✔
168
            }
169

UNCOV
170
            if (\in_array(lcfirst(substr($methodName, 3)), $ignoredOptions, true)) {
34✔
UNCOV
171
                continue;
8✔
172
            }
173

UNCOV
174
            if (!method_exists($operation, $methodName) || null !== $operation->{$methodName}()) {
34✔
UNCOV
175
                continue;
34✔
176
            }
177

UNCOV
178
            if (null === ($value = $resource->{$methodName}())) {
34✔
UNCOV
179
                continue;
34✔
180
            }
181

UNCOV
182
            $operation = $operation->{'with'.substr($methodName, 3)}($value);
34✔
183
        }
184

UNCOV
185
        $operation = $operation->withExtraProperties(array_merge(
34✔
UNCOV
186
            $resource->getExtraProperties(),
34✔
UNCOV
187
            $operation->getExtraProperties(),
34✔
UNCOV
188
            $generated ? ['generated_operation' => true] : []
34✔
UNCOV
189
        ));
34✔
190

191
        // Add global defaults attributes to the operation
UNCOV
192
        $operation = $this->addGlobalDefaults($operation);
34✔
193

UNCOV
194
        if ($operation instanceof GraphQlOperation) {
34✔
UNCOV
195
            if (!$operation->getName()) {
31✔
196
                throw new RuntimeException('No GraphQL operation name.');
×
197
            }
198

UNCOV
199
            if ($operation instanceof Mutation) {
31✔
UNCOV
200
                $operation = $operation->withDescription(ucfirst("{$operation->getName()}s a {$resource->getShortName()}."));
29✔
201
            }
202

UNCOV
203
            return [$operation->getName(), $operation];
31✔
204
        }
205

UNCOV
206
        if (!$operation instanceof HttpOperation) {
34✔
207
            throw new RuntimeException(\sprintf('Operation should be an instance of "%s"', HttpOperation::class));
×
208
        }
209

UNCOV
210
        if (!$operation->getName() && $operation->getRouteName()) {
34✔
211
            /** @var HttpOperation $operation */
UNCOV
212
            $operation = $operation->withName($operation->getRouteName());
3✔
213
        }
214

UNCOV
215
        $operationName = $operation->getName() ?? $this->getDefaultOperationName($operation, $resource->getClass());
34✔
216

UNCOV
217
        return [
34✔
UNCOV
218
            $operationName,
34✔
UNCOV
219
            $operation,
34✔
UNCOV
220
        ];
34✔
221
    }
222

223
    private function getDefaultShortname(string $resourceClass): string
224
    {
225
        return (false !== $pos = strrpos($resourceClass, '\\')) ? substr($resourceClass, $pos + 1) : $resourceClass;
×
226
    }
227

228
    private function getDefaultOperationName(HttpOperation $operation, string $resourceClass): string
229
    {
UNCOV
230
        $path = ($operation->getRoutePrefix() ?? '').($operation->getUriTemplate() ?? '');
33✔
231

UNCOV
232
        return \sprintf(
33✔
UNCOV
233
            '_api_%s_%s%s',
33✔
UNCOV
234
            $path ?: ($operation->getShortName() ?? $this->getDefaultShortname($resourceClass)),
33✔
UNCOV
235
            strtolower($operation->getMethod()),
33✔
UNCOV
236
            $operation instanceof CollectionOperationInterface ? '_collection' : '');
33✔
237
    }
238
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc