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

api-platform / core / 15133993414

20 May 2025 09:30AM UTC coverage: 26.313% (-1.2%) from 27.493%
15133993414

Pull #7161

github

web-flow
Merge e2c03d45f into 5459ba375
Pull Request #7161: fix(metadata): infer parameter string type from schema

0 of 2 new or added lines in 1 file covered. (0.0%)

11019 existing lines in 363 files now uncovered.

12898 of 49018 relevant lines covered (26.31%)

34.33 hits per line

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

98.33
/src/State/Processor/RespondProcessor.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\State\Processor;
15

16
use ApiPlatform\Metadata\Exception\HttpExceptionInterface;
17
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
18
use ApiPlatform\Metadata\Exception\ItemNotFoundException;
19
use ApiPlatform\Metadata\Exception\RuntimeException;
20
use ApiPlatform\Metadata\HttpOperation;
21
use ApiPlatform\Metadata\IriConverterInterface;
22
use ApiPlatform\Metadata\Operation;
23
use ApiPlatform\Metadata\Operation\Factory\OperationMetadataFactoryInterface;
24
use ApiPlatform\Metadata\Put;
25
use ApiPlatform\Metadata\ResourceClassResolverInterface;
26
use ApiPlatform\Metadata\UrlGeneratorInterface;
27
use ApiPlatform\Metadata\Util\ClassInfoTrait;
28
use ApiPlatform\Metadata\Util\CloneTrait;
29
use ApiPlatform\State\ProcessorInterface;
30
use Symfony\Component\HttpFoundation\Response;
31
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface as SymfonyHttpExceptionInterface;
32

33
/**
34
 * Serializes data.
35
 *
36
 * @author Kévin Dunglas <dunglas@gmail.com>
37
 */
38
final class RespondProcessor implements ProcessorInterface
39
{
40
    use ClassInfoTrait;
41
    use CloneTrait;
42

43
    public const METHOD_TO_CODE = [
44
        'POST' => Response::HTTP_CREATED,
45
        'DELETE' => Response::HTTP_NO_CONTENT,
46
    ];
47

48
    public function __construct(
49
        private ?IriConverterInterface $iriConverter = null,
50
        private readonly ?ResourceClassResolverInterface $resourceClassResolver = null,
51
        private readonly ?OperationMetadataFactoryInterface $operationMetadataFactory = null,
52
    ) {
UNCOV
53
    }
802✔
54

55
    public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = [])
56
    {
UNCOV
57
        if ($data instanceof Response || !$operation instanceof HttpOperation) {
795✔
UNCOV
58
            return $data;
15✔
59
        }
60

UNCOV
61
        if (!($request = $context['request'] ?? null)) {
782✔
62
            return $data;
×
63
        }
64

UNCOV
65
        $headers = [
782✔
UNCOV
66
            'Content-Type' => \sprintf('%s; charset=utf-8', $request->getMimeType($request->getRequestFormat())),
782✔
UNCOV
67
            'Vary' => 'Accept',
782✔
UNCOV
68
            'X-Content-Type-Options' => 'nosniff',
782✔
UNCOV
69
            'X-Frame-Options' => 'deny',
782✔
UNCOV
70
        ];
782✔
71

UNCOV
72
        $exception = $request->attributes->get('exception');
782✔
UNCOV
73
        if (($exception instanceof HttpExceptionInterface || $exception instanceof SymfonyHttpExceptionInterface) && $exceptionHeaders = $exception->getHeaders()) {
782✔
UNCOV
74
            $headers = array_merge($headers, $exceptionHeaders);
1✔
75
        }
76

UNCOV
77
        if ($operationHeaders = $operation->getHeaders()) {
782✔
UNCOV
78
            $headers = array_merge($headers, $operationHeaders);
2✔
79
        }
80

UNCOV
81
        $status = $operation->getStatus();
782✔
82

UNCOV
83
        if ($sunset = $operation->getSunset()) {
782✔
84
            $headers['Sunset'] = (new \DateTimeImmutable($sunset))->format(\DateTimeInterface::RFC1123);
2✔
85
        }
86

UNCOV
87
        if ($acceptPatch = $operation->getAcceptPatch()) {
782✔
UNCOV
88
            $headers['Accept-Patch'] = $acceptPatch;
175✔
89
        }
90

UNCOV
91
        $method = $request->getMethod();
782✔
UNCOV
92
        $originalData = $context['original_data'] ?? null;
782✔
93

UNCOV
94
        $outputMetadata = $operation->getOutput() ?? ['class' => $operation->getClass()];
782✔
UNCOV
95
        $hasOutput = \is_array($outputMetadata) && \array_key_exists('class', $outputMetadata) && null !== $outputMetadata['class'];
782✔
UNCOV
96
        $hasData = !$hasOutput ? false : ($this->resourceClassResolver && $originalData && \is_object($originalData) && $this->resourceClassResolver->isResourceClass($this->getObjectClass($originalData)));
782✔
97

UNCOV
98
        if ($hasData) {
782✔
UNCOV
99
            $isAlternateResourceMetadata = $operation->getExtraProperties()['is_alternate_resource_metadata'] ?? false;
385✔
UNCOV
100
            $canonicalUriTemplate = $operation->getExtraProperties()['canonical_uri_template'] ?? null;
385✔
101

102
            if (
UNCOV
103
                !isset($headers['Location'])
385✔
UNCOV
104
                && 300 <= $status && $status < 400
385✔
UNCOV
105
                && ($isAlternateResourceMetadata || $canonicalUriTemplate)
385✔
106
            ) {
UNCOV
107
                $canonicalOperation = $operation;
1✔
UNCOV
108
                if ($this->operationMetadataFactory && null !== $canonicalUriTemplate) {
1✔
UNCOV
109
                    $canonicalOperation = $this->operationMetadataFactory->create($canonicalUriTemplate, $context);
1✔
110
                }
111

UNCOV
112
                if ($this->iriConverter) {
1✔
UNCOV
113
                    $headers['Location'] = $this->iriConverter->getIriFromResource($originalData, UrlGeneratorInterface::ABS_PATH, $canonicalOperation);
1✔
114
                }
UNCOV
115
            } elseif ('PUT' === $method && !$request->attributes->get('previous_data') && null === $status && ($operation instanceof Put && ($operation->getAllowCreate() ?? false))) {
385✔
116
                $status = 201;
2✔
117
            }
118
        }
119

UNCOV
120
        $status ??= self::METHOD_TO_CODE[$method] ?? 200;
782✔
121

UNCOV
122
        $requestParts = parse_url($request->getRequestUri());
782✔
UNCOV
123
        if ($this->iriConverter && !isset($headers['Content-Location'])) {
782✔
124
            try {
UNCOV
125
                $iri = null;
780✔
UNCOV
126
                if ($hasData) {
780✔
UNCOV
127
                    $iri = $this->iriConverter->getIriFromResource($originalData);
385✔
UNCOV
128
                } elseif ($operation->getClass()) {
415✔
UNCOV
129
                    $iri = $this->iriConverter->getIriFromResource($operation->getClass(), UrlGeneratorInterface::ABS_PATH, $operation);
407✔
130
                }
131

UNCOV
132
                if ($iri && 'GET' !== $method) {
742✔
UNCOV
133
                    $location = \sprintf('%s.%s', $iri, $request->getRequestFormat());
151✔
UNCOV
134
                    if (isset($requestParts['query'])) {
151✔
135
                        $location .= '?'.$requestParts['query'];
12✔
136
                    }
137

UNCOV
138
                    $headers['Content-Location'] = $location;
151✔
UNCOV
139
                    if ((201 === $status || (300 <= $status && $status < 400)) && 'POST' === $method && !isset($headers['Location'])) {
151✔
UNCOV
140
                        $headers['Location'] = $iri;
742✔
141
                    }
142
                }
UNCOV
143
            } catch (InvalidArgumentException|ItemNotFoundException|RuntimeException) {
38✔
144
            }
145
        }
146

UNCOV
147
        return new Response(
782✔
UNCOV
148
            $data,
782✔
UNCOV
149
            $status,
782✔
UNCOV
150
            $headers
782✔
UNCOV
151
        );
782✔
152
    }
153
}
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