From 42e66d8b274d50f629697d6e9d8a4d80e3bfa1f4 Mon Sep 17 00:00:00 2001 From: Vlastimil Pecinka Date: Mon, 25 Sep 2023 20:30:00 +0200 Subject: [PATCH] format segment name, close #365 (#366) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * format segment name, close php-opencloud/openstack#365 --------- Co-authored-by: Vlastimil Pečínka --- src/ObjectStore/v1/Models/Container.php | 34 +++++++++++++++---- .../ObjectStore/v1/Models/ContainerTest.php | 15 ++++++-- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/ObjectStore/v1/Models/Container.php b/src/ObjectStore/v1/Models/Container.php index 55068184..4736d2a1 100644 --- a/src/ObjectStore/v1/Models/Container.php +++ b/src/ObjectStore/v1/Models/Container.php @@ -180,6 +180,22 @@ public function objectExists(string $name): bool } } + /** + * Verifies if provied segment index format for DLOs is valid. + * + * @param string $fmt The format of segment index name, e.g. %05d for 00001, 00002, etc. + * + * @return bool TRUE if the format is valid, FALSE if it is not + */ + public function isValidSegmentIndexFormat($fmt) + { + $testValue1 = sprintf($fmt, 1); + $testValue2 = sprintf($fmt, 10); + + // Test if different results of the same string length + return ($testValue1 !== $testValue2) && (strlen($testValue1) === strlen($testValue2)); + } + /** * Creates a single object according to the values provided. * @@ -197,11 +213,12 @@ public function createObject(array $data): StorageObject * container. When this completes, a manifest file is uploaded which references the prefix of the segments, * allowing concatenation when a request is executed against the manifest. * - * @param array $data {@see \OpenStack\ObjectStore\v1\Api::putObject} - * @param int $data['segmentSize'] The size in Bytes of each segment - * @param string $data['segmentContainer'] The container to which each segment will be uploaded - * @param string $data['segmentPrefix'] The prefix that will come before each segment. If omitted, a default - * is used: name/timestamp/filesize + * @param array $data {@see \OpenStack\ObjectStore\v1\Api::putObject} + * @param int $data['segmentSize'] The size in Bytes of each segment + * @param string $data['segmentContainer'] The container to which each segment will be uploaded + * @param string $data['segmentPrefix'] The prefix that will come before each segment. If omitted, a default + * is used: name/timestamp/filesize + * @param string $data['segmentIndexFormat'] The format of segment index name, default %05d - 00001, 00002, etc. */ public function createLargeObject(array $data): StorageObject { @@ -213,6 +230,11 @@ public function createLargeObject(array $data): StorageObject $segmentPrefix = isset($data['segmentPrefix']) ? $data['segmentPrefix'] : sprintf('%s/%s/%d', $data['name'], microtime(true), $stream->getSize()); + $segmentIndexFormat = isset($data['segmentIndexFormat']) ? $data['segmentIndexFormat'] : '%05d'; + + if (!$this->isValidSegmentIndexFormat($segmentIndexFormat)) { + throw new \InvalidArgumentException('The provided segmentIndexFormat is not valid.'); + } /** @var \OpenStack\ObjectStore\v1\Service $service */ $service = $this->getService(); @@ -226,7 +248,7 @@ public function createLargeObject(array $data): StorageObject while (!$stream->eof() && $count < $totalSegments) { $promises[] = $this->model(StorageObject::class)->createAsync([ - 'name' => sprintf('%s/%d', $segmentPrefix, ++$count), + 'name' => sprintf('%s/'.$segmentIndexFormat, $segmentPrefix, ++$count), 'stream' => new LimitStream($stream, $segmentSize, ($count - 1) * $segmentSize), 'containerName' => $segmentContainer, ]); diff --git a/tests/unit/ObjectStore/v1/Models/ContainerTest.php b/tests/unit/ObjectStore/v1/Models/ContainerTest.php index c34cd924..be363cab 100644 --- a/tests/unit/ObjectStore/v1/Models/ContainerTest.php +++ b/tests/unit/ObjectStore/v1/Models/ContainerTest.php @@ -208,6 +208,14 @@ public function test_other_exceptions_are_thrown() $this->container->objectExists('bar'); } + public function test_valid_segment_index_format() + { + self::assertTrue($this->container->isValidSegmentIndexFormat("%03d")); + self::assertTrue($this->container->isValidSegmentIndexFormat("%05d")); + self::assertFalse($this->container->isValidSegmentIndexFormat("%d")); + self::assertFalse($this->container->isValidSegmentIndexFormat("d")); + } + public function test_it_chunks_according_to_provided_segment_size() { $stream = function_exists('\GuzzleHttp\Psr7\stream_for') @@ -220,6 +228,7 @@ public function test_it_chunks_according_to_provided_segment_size() 'segmentSize' => 10, 'segmentPrefix' => 'objectPrefix', 'segmentContainer' => 'segments', + 'segmentIndexFormat' => '%03d', ]; // check container creation @@ -235,9 +244,9 @@ public function test_it_chunks_according_to_provided_segment_size() $this->setupMock('PUT', 'segments', null, [], new Response(201)); // The stream has size 24 so we expect three segments. - $this->setupMock('PUT', 'segments/objectPrefix/1', $stream->read(10), [], new Response(201)); - $this->setupMock('PUT', 'segments/objectPrefix/2', $stream->read(10), [], new Response(201)); - $this->setupMock('PUT', 'segments/objectPrefix/3', $stream->read(10), [], new Response(201)); + $this->setupMock('PUT', 'segments/objectPrefix/001', $stream->read(10), [], new Response(201)); + $this->setupMock('PUT', 'segments/objectPrefix/002', $stream->read(10), [], new Response(201)); + $this->setupMock('PUT', 'segments/objectPrefix/003', $stream->read(10), [], new Response(201)); $this->setupMock('PUT', 'test/object', null, ['X-Object-Manifest' => 'segments/objectPrefix'], new Response(201)); $stream->rewind();