Skip to content

Commit 1992662

Browse files
committed
endpoints mostly working in real tests - multipart uploads failing
1 parent d3f8470 commit 1992662

8 files changed

Lines changed: 69 additions & 17 deletions

File tree

src/Controller/File/GetElementFileController.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ public function getElementFile(string $id): BinaryStreamResponse
5858

5959
$fileOperation = $this->fileOperationFactory->createFileOperationFromElement($element);
6060

61+
// print_r($fileOperation);
62+
// exit;
63+
6164
$doesFileExist = $this->s3Service->existsFile($fileOperation);
6265
if (false === $doesFileExist) {
6366
throw $this->client404NotFoundExceptionFactory->createFromTemplate();

src/Controller/Upload/HeadUploadController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public function headUpload(string $id): Response
5151
throw $this->client404NotFoundExceptionFactory->createFromTemplate();
5252
}
5353

54-
if ($uploadElement->getUploadOwner() !== $userId) {
54+
if ($uploadElement->getUploadOwner()->toString() !== $userId->toString()) {
5555
throw $this->client404NotFoundExceptionFactory->createFromTemplate();
5656
}
5757

src/Controller/Upload/PatchUploadController.php

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,9 @@ public function patchUpload(string $id, Request $request): Response
7878
throw $this->client404NotFoundExceptionFactory->createFromTemplate();
7979
}
8080

81-
if ($uploadElement->getUploadOwner() !== $userId) {
81+
if ($uploadElement->getUploadOwner()?->toString() !== $userId->toString()) {
8282
throw $this->client404NotFoundExceptionFactory->createFromTemplate();
8383
}
84-
8584
if ($uploadElement->getExpires() < new DateTime()) {
8685
throw $this->client410GoneExceptionFactory->createFromTemplate();
8786
}
@@ -99,7 +98,7 @@ public function patchUpload(string $id, Request $request): Response
9998

10099
$patchResource = $request->getContent(true);
101100

102-
$currentChunkIndex = $uploadElement->getAlreadyUploadedChunks();
101+
$currentChunkIndex = $uploadElement->getAlreadyUploadedChunks() + 1;
103102
$nextChunkKey = $this->fileService->getUploadBucketKey($uploadId, $currentChunkIndex);
104103

105104
$this->s3Client->putObject([
@@ -119,6 +118,8 @@ public function patchUpload(string $id, Request $request): Response
119118
throw $this->server500LogicExceptionFactory->createFromTemplate('Unable to read content length of created chunk.');
120119
}
121120

121+
// update uploadelement to update chunk index +1 and upload offset + n bytes
122+
122123
$canCreateFile = false;
123124

124125
if (null !== $partialUploadRequest->getContentLength()
@@ -133,8 +134,15 @@ public function patchUpload(string $id, Request $request): Response
133134
$uploadElement->setUploadComplete(true);
134135
}
135136

136-
$uploadElement->setUploadOffset($uploadElement->getUploadOffset() + $contentLength);
137-
$uploadElement->setAlreadyUploadedChunks($uploadElement->getAlreadyUploadedChunks() + 1);
137+
138+
// print_r($uploadElement);
139+
140+
$uploadElement = $uploadElement
141+
->setUploadOffset($uploadElement->getUploadOffset() + $contentLength)
142+
->setAlreadyUploadedChunks($uploadElement->getAlreadyUploadedChunks() + 1);
143+
144+
// print_r($uploadElement);
145+
// exit;
138146

139147
$this->elementManager->merge($uploadElement);
140148
$this->elementManager->flush();
@@ -168,14 +176,15 @@ public function createFile(UploadElement $uploadElement, UuidInterface $uploadTa
168176
$parts = [];
169177

170178
try {
171-
for ($i = 0; $i <= $uploadElement->getAlreadyUploadedChunks(); ++$i) {
179+
for ($i = 1; $i <= $uploadElement->getAlreadyUploadedChunks(); ++$i) {
172180
$sourceKey = $this->fileService->getUploadBucketKey($uploadId, $i);
173181
// todo: possible bug with upload bucket vs storage bucket; needs to be tested live
174182
$copyResult = $this->s3Client->uploadPartCopy([
175-
'Bucket' => $this->emberNexusConfiguration->getFileS3UploadBucket(),
183+
'Bucket' => $this->emberNexusConfiguration->getFileS3StorageBucket(),
176184
'Key' => $targetKey,
177185
'UploadId' => $multipartUploadId,
178-
'PartNumber' => $i + 1,
186+
// 'PartNumber' => $i + 1,
187+
'PartNumber' => $i,
179188
'CopySource' => sprintf('%s/%s', $this->emberNexusConfiguration->getFileS3UploadBucket(), $sourceKey),
180189
]);
181190

@@ -185,7 +194,8 @@ public function createFile(UploadElement $uploadElement, UuidInterface $uploadTa
185194
}
186195

187196
$parts[] = [
188-
'PartNumber' => $i + 1,
197+
// 'PartNumber' => $i + 1,
198+
'PartNumber' => $i,
189199
'ETag' => $copyPartResult->getETag(),
190200
];
191201
}
@@ -199,6 +209,7 @@ public function createFile(UploadElement $uploadElement, UuidInterface $uploadTa
199209
],
200210
]);
201211

212+
// important todos:
202213
// todo: delete original file, if it a) existed and b) had a different file extension
203214
// todo: set file property to actual element, merge and flush it?
204215
} catch (Throwable $e) {

src/Factory/Response/NoContentResponseFactory.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ public function __construct(
1515
) {
1616
}
1717

18+
public function createNoContentResponseWithLocationHeader(string $location): NoContentResponse
19+
{
20+
$response = new NoContentResponse();
21+
$headers = $response->headers;
22+
23+
$headers->set('Location', $location);
24+
return $response;
25+
}
26+
1827
public function createNoContentResponseWithResumableUploadHeaders(UploadElement $uploadElement): NoContentResponse
1928
{
2029
$response = new NoContentResponse();

src/Factory/Type/Request/ResumableUploadRequestFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public function createResumableUploadRequestFromRequest(Request $request, UuidIn
2828
$extension = $this->headerParseService->getExtensionFromHeaders($headers);
2929
$content = $request->getContent(true);
3030

31-
if (null !== $uploadLength && null !== $contentLength && $uploadLength !== $contentLength) {
31+
if (null !== $uploadLength && null !== $contentLength && $uploadLength !== $contentLength && $isUploadComplete === true) {
3232
throw $this->client400BadContentExceptionFactory->createFromDetail("Inconsistent length values provided in headers 'Content-Length' and 'Upload-Length'.");
3333
}
3434

src/Service/S3Service.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,13 @@ public function __construct(
2424
) {
2525
}
2626

27-
public function uploadFileChunk(UploadFileChunkOperation $uploadFileChunkOperation): void
27+
/**
28+
* @param UploadFileChunkOperation $uploadFileChunkOperation
29+
* @return int length of the uploaded chunk
30+
* @throws \App\Exception\Client400BadContentException
31+
* @throws \App\Exception\Server500LogicErrorException
32+
*/
33+
public function uploadFileChunk(UploadFileChunkOperation $uploadFileChunkOperation): int
2834
{
2935
$this->s3Client->putObject([
3036
'Bucket' => $uploadFileChunkOperation->getUploadBucket(),
@@ -50,6 +56,8 @@ public function uploadFileChunk(UploadFileChunkOperation $uploadFileChunkOperati
5056
throw $this->client400BadContentExceptionFactory->createFromDetail(sprintf('Inconsistent length values between provided content-length (%d) and actual content length (%d) detected.', $providedContentLength, $uploadContentLength));
5157
}
5258
}
59+
60+
return $uploadContentLength;
5361
}
5462

5563
/**

src/Service/UploadCreationService.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use App\Contract\NodeElementInterface;
88
use App\Contract\RelationElementInterface;
99
use App\EventSystem\ElementFileReplace\Event\ElementFileReplaceEvent;
10+
use App\Factory\Response\NoContentResponseFactory;
1011
use App\Factory\Type\Request\ResumableUploadRequestFactory;
1112
use App\Factory\Type\S3\UploadFileChunkOperationFactory;
1213
use App\Factory\Type\S3\UploadFileOperationFactory;
@@ -23,6 +24,7 @@
2324
use Safe\DateTime;
2425
use Symfony\Component\HttpFoundation\Request;
2526
use Symfony\Component\HttpFoundation\Response;
27+
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
2628
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
2729

2830
/**
@@ -40,6 +42,8 @@ public function __construct(
4042
private UploadFileChunkOperationFactory $uploadFileChunkOperationFactory,
4143
private ResumableUploadRequestFactory $resumableUploadRequestFactory,
4244
private EventDispatcherInterface $eventDispatcher,
45+
private NoContentResponseFactory $noContentResponseFactory,
46+
private UrlGeneratorInterface $urlGenerator,
4347
) {
4448
}
4549

@@ -70,6 +74,7 @@ private function setOrReplaceElementFileDirectly(
7074
// todo: replace manual array with fileProperty instance
7175
$element->addProperty('file', [
7276
'contentLength' => $uploadFileOperation->getContentLength(),
77+
'extension' => $resumableUploadRequest->getExtension()
7378
]);
7479
$this->elementManager->merge($element);
7580
$this->elementManager->flush();
@@ -82,22 +87,32 @@ private function createNewResumableUpload(ResumableUploadRequest $resumableUploa
8287
$expires = (new DateTime())->add(new DateInterval(sprintf('PT%sS', $this->emberNexusConfiguration->getFileUploadExpiresInSecondsAfterFirstRequest())));
8388

8489
$uploadId = Uuid::uuid4();
85-
// todo: try to parse extension, and persist it
8690
$uploadElement = new UploadElement();
8791
$uploadElement
8892
->setId($uploadId)
93+
->setLabel('Upload')
8994
->setUploadOwner($userId)
9095
->setUploadTarget($resumableUploadRequest->getElementId())
9196
->setExtension($resumableUploadRequest->getExtension())
9297
->setExpires($expires)
9398
->setUploadLength($resumableUploadRequest->getUploadLength());
9499

95100
$uploadFileChunkOperation = $this->uploadFileChunkOperationFactory->createUploadFileChunkOperationFromResumableUploadRequest($resumableUploadRequest);
96-
$this->s3Service->uploadFileChunk($uploadFileChunkOperation);
101+
$chunkLength = $this->s3Service->uploadFileChunk($uploadFileChunkOperation);
102+
103+
$uploadElement->setUploadOffset($chunkLength);
104+
$uploadElement->setAlreadyUploadedChunks(1);
97105

98106
$this->elementManager->merge($uploadElement);
99107
$this->elementManager->flush();
100108

101-
return new NoContentResponse();
109+
$location = $this->urlGenerator->generate(
110+
'head-upload',
111+
[
112+
'id' => $uploadId->toString()
113+
],
114+
UrlGeneratorInterface::ABSOLUTE_URL
115+
);
116+
return $this->noContentResponseFactory->createNoContentResponseWithLocationHeader($location);
102117
}
103118
}

src/Type/UploadElement.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use App\Contract\RelationElementInterface;
99
use App\Service\FileService;
1010
use DateTime;
11+
use DateTimeImmutable;
1112
use Exception;
1213
use Ramsey\Uuid\Uuid;
1314
use Ramsey\Uuid\UuidInterface;
@@ -46,6 +47,8 @@ public static function createFromElement(NodeElementInterface|RelationElementInt
4647
if ('Upload' !== $label) {
4748
throw new Exception(sprintf('Can not cast element of type %s to upload.', $label));
4849
}
50+
$upload->setLabel('Upload');
51+
$upload->setId($element->getId());
4952

5053
$properties = $element->getProperties();
5154

@@ -119,6 +122,9 @@ public static function createFromElement(NodeElementInterface|RelationElementInt
119122
if ($expires instanceof DateTime) {
120123
$upload->setExpires($expires);
121124
}
125+
if ($expires instanceof DateTimeImmutable) {
126+
$upload->setExpires(DateTime::createFromImmutable($expires));
127+
}
122128
}
123129

124130
return $upload;
@@ -170,7 +176,7 @@ public function getUploadTarget(): ?UuidInterface
170176

171177
public function setUploadTarget(?UuidInterface $uploadTarget): static
172178
{
173-
$this->addProperty('uploadTarget', $uploadTarget);
179+
$this->addProperty('uploadTarget', $uploadTarget?->toString());
174180
$this->uploadTarget = $uploadTarget;
175181

176182
return $this;
@@ -196,7 +202,7 @@ public function getUploadOwner(): ?UuidInterface
196202

197203
public function setUploadOwner(?UuidInterface $uploadOwner): static
198204
{
199-
$this->addProperty('uploadOwner', $uploadOwner);
205+
$this->addProperty('uploadOwner', $uploadOwner?->toString());
200206
$this->uploadOwner = $uploadOwner;
201207

202208
return $this;

0 commit comments

Comments
 (0)