diff --git a/.gitignore b/.gitignore index b3c274d..55de092 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /vendor composer.lock .phpunit* +/docs/build/ \ No newline at end of file diff --git a/phpdoc.dist.xml b/phpdoc.dist.xml new file mode 100644 index 0000000..8e02c39 --- /dev/null +++ b/phpdoc.dist.xml @@ -0,0 +1,19 @@ + + + + docs/build/api + docs/build/cache + + + + + src + + + + \ No newline at end of file diff --git a/src/Classes/AbstractObjectInfo.php b/src/Classes/AbstractObjectInfo.php index dc0e13a..5886a9d 100644 --- a/src/Classes/AbstractObjectInfo.php +++ b/src/Classes/AbstractObjectInfo.php @@ -7,21 +7,15 @@ use function sizeof; use function rawurlencode; -//use ArrayAccess; -//use JsonSerializable; use RuntimeException; -//use Zaxbux\BackblazeB2\Traits\ProxyArrayAccessToPropertiesTrait; - /** * @link https://www.backblaze.com/b2/docs/files.html#fileInfo * - + * @package Zaxbux\BackblazeB2\Classes */ abstract class AbstractObjectInfo { -//final class FileInfo implements ArrayAccess, JsonSerializable { - //use ProxyArrayAccessToPropertiesTrait; public const HEADER_PREFIX = 'X-Bz-Info-'; diff --git a/src/Classes/FilePathInfo.php b/src/Classes/FilePathInfo.php index c20873f..11cd923 100644 --- a/src/Classes/FilePathInfo.php +++ b/src/Classes/FilePathInfo.php @@ -7,6 +7,7 @@ use function pathinfo; +/** @package Zaxbux\BackblazeB2\Classes */ final class FilePathInfo { diff --git a/src/Client.php b/src/Client.php index 97714cf..87219bb 100644 --- a/src/Client.php +++ b/src/Client.php @@ -8,14 +8,23 @@ use GuzzleHttp\Client as GuzzleClient; use GuzzleHttp\ClientInterface; use Zaxbux\BackblazeB2\Config; -use Zaxbux\BackblazeB2\Http\Middleware\ApplyAuthorizationMiddleware; -use Zaxbux\BackblazeB2\Http\Middleware\ExceptionMiddleware; -use Zaxbux\BackblazeB2\Http\Middleware\RetryMiddleware; +use Zaxbux\BackblazeB2\Http\Endpoint; +use Zaxbux\BackblazeB2\Http\Middleware\{ + ApplyAuthorizationMiddleware, + ExceptionMiddleware, + RetryMiddleware, +}; +use Zaxbux\BackblazeB2\Operations\{ + ApplicationKeyOperationsTrait, + BucketOperationsTrait, + DownloadOperationsTrait, + FileOperationsTrait, + LargeFileOperationsTrait, + UploadOperationsTrait, +}; use Zaxbux\BackblazeB2\Interfaces\AuthorizationCacheInterface; use Zaxbux\BackblazeB2\Object\AccountAuthorization; -use Zaxbux\BackblazeB2\Operations\ApplicationKeyOperationsTrait; -use Zaxbux\BackblazeB2\Operations\BucketOperationsTrait; -use Zaxbux\BackblazeB2\Operations\FileOperationsTrait; +use Zaxbux\BackblazeB2\Traits\ApplyToAllFileVersionsTrait; /** * API Client for Backblaze B2. @@ -30,8 +39,12 @@ class Client public const B2_API_VERSION = 'b2api/v2/'; use FileOperationsTrait; + use LargeFileOperationsTrait; use BucketOperationsTrait; use ApplicationKeyOperationsTrait; + use UploadOperationsTrait; + use DownloadOperationsTrait; + use ApplyToAllFileVersionsTrait; /** @var \Zaxbux\BackblazeB2\Config */ protected $config; @@ -92,7 +105,7 @@ public function __construct($config) * @param ClientInterface $client */ public function authorizeAccount(): AccountAuthorization { - $response = $this->http->request('GET', Client::BASE_URI . Client::B2_API_VERSION . 'b2_authorize_account', [ + $response = $this->http->request('GET', Client::BASE_URI . Client::B2_API_VERSION . Endpoint::AUTHORIZE_ACCOUNT, [ 'headers' => [ 'Authorization' => Utils::basicAuthorization($this->config->applicationKeyId(), $this->config->applicationKey()), ], @@ -134,7 +147,7 @@ protected function createDefaultHttpClient(): ClientInterface { }*/ $stack->push(new ExceptionMiddleware(), 'exception_handler'); - $stack->push(new ApplyAuthorizationMiddleware($this), 'b2_auth'); + $stack->push(new ApplyAuthorizationMiddleware($this), 'b2_authorization'); $stack->push(new RetryMiddleware($this->config), 'retry'); $client = new GuzzleClient([ @@ -143,7 +156,6 @@ protected function createDefaultHttpClient(): ClientInterface { 'allow_redirects' => false, 'handler' => $stack, 'headers' => [ - //'Accept' => 'application/json, */*;q=0.8', 'User-Agent' => Utils::getUserAgent($this->config->applicationName()), ], ]); @@ -164,7 +176,7 @@ public function getAllowedBucketName(): ?string /** * @see __construct() */ - public static function instance($config): Client + public static function create($config): Client { return new static($config); } diff --git a/src/Config.php b/src/Config.php index 12ca625..7f7a1f8 100644 --- a/src/Config.php +++ b/src/Config.php @@ -13,6 +13,17 @@ */ class Config { + public const DEFAULTS = [ + 'applicationName' => '', + 'handler' => null, + 'middleware' => [], + 'useHttpErrors' => null, + 'maxRetries' => 4, + 'maxRetryDelay' => 64, + 'maxFileCount' => 1000, + 'maxKeyCount' => 1000, + 'useSSEHeaders' => false, + ]; /** * The identifier for the key. The account ID can also be used. @@ -30,7 +41,7 @@ class Config * Application name, included in the User-Agent HTTP header. * @var string */ - private $applicationName = ''; + private $applicationName; /** * Custom Guzzle handler or handler stack. @@ -48,16 +59,16 @@ class Config * Optional middleware to add to the GuzzleHttp handler stack. * @var array */ - public $middleware = []; + public $middleware; /** @var bool */ - public $useHttpErrors = false; + public $useHttpErrors; /** * Number of times to retry an API call before throwing an exception. * @var int */ - public $maxRetries = 4; + public $maxRetries; /** * Maximum amount of time, in seconds, to wait before retrying a failed request. @@ -66,31 +77,31 @@ class Config * * @var int */ - public $maxRetryDelay = 64; + public $maxRetryDelay; /** * Download files with Server-Side Encryption headers instead of using query parameters. * @var false */ - public $useSSEHeaders = false; + public $useSSEHeaders; /** * Maximum number of application keys to return per call. * @var int */ - public $maxKeyCount = 1000; + public $maxKeyCount; /** * Maximum number of files to return per call. * @var int */ - public $maxFileCount = 1000; + public $maxFileCount; /** * Size limit to determine if the upload will use the large-file process. * @var int */ - public $largeFileUploadCustomMinimum = null; //200 * 1024 * 1024; + public $largeFileUploadCustomMinimum; //200 * 1024 * 1024; /** * An object that implements `AuthorizationCacheInterface` for caching @@ -179,8 +190,11 @@ public static function fromArray($data): Config } private function setOptions(array $options) { - $this->handler = $options['handler'] ?? null; - $this->maxRetries = $options['maxRetries'] ?? 4; + $options = array_merge(static::DEFAULTS, $options); + + $this->handler = $options['handler']; + $this->maxRetries = $options['maxRetries']; + $this->applicationName = $options['applicationName']; $this->authorizationCache = $options['authorizationCache'] ?? new BuiltinAuthorizationCache(); } } \ No newline at end of file diff --git a/src/Exceptions/NotFoundException.php b/src/Exceptions/NotFoundException.php index c1b596c..f396e49 100644 --- a/src/Exceptions/NotFoundException.php +++ b/src/Exceptions/NotFoundException.php @@ -2,6 +2,7 @@ namespace Zaxbux\BackblazeB2\Exceptions; +/** @package Zaxbux\BackblazeB2\Exceptions */ class NotFoundException extends \Exception { } diff --git a/src/Exceptions/Request/AccessDeniedException.php b/src/Exceptions/Request/AccessDeniedException.php index 2c9572e..b005760 100644 --- a/src/Exceptions/Request/AccessDeniedException.php +++ b/src/Exceptions/Request/AccessDeniedException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class AccessDeniedException extends B2APIException {} diff --git a/src/Exceptions/Request/B2APIException.php b/src/Exceptions/Request/B2APIException.php index e84de06..81559e2 100644 --- a/src/Exceptions/Request/B2APIException.php +++ b/src/Exceptions/Request/B2APIException.php @@ -8,6 +8,7 @@ use Psr\Http\Message\ResponseInterface; use Throwable; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class B2APIException extends RequestException { private $statusCode; diff --git a/src/Exceptions/Request/BadAuthTokenException.php b/src/Exceptions/Request/BadAuthTokenException.php index 40fc3fa..804b0d6 100644 --- a/src/Exceptions/Request/BadAuthTokenException.php +++ b/src/Exceptions/Request/BadAuthTokenException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class BadAuthTokenException extends B2APIException {} diff --git a/src/Exceptions/Request/BadRequestException.php b/src/Exceptions/Request/BadRequestException.php index 8f2d837..65f285e 100644 --- a/src/Exceptions/Request/BadRequestException.php +++ b/src/Exceptions/Request/BadRequestException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class BadRequestException extends B2APIException {} diff --git a/src/Exceptions/Request/CapExceededException.php b/src/Exceptions/Request/CapExceededException.php index 98d3611..e6d746f 100644 --- a/src/Exceptions/Request/CapExceededException.php +++ b/src/Exceptions/Request/CapExceededException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class CapExceededException extends B2APIException {} diff --git a/src/Exceptions/Request/ConflictException.php b/src/Exceptions/Request/ConflictException.php index abeeb17..0414b10 100644 --- a/src/Exceptions/Request/ConflictException.php +++ b/src/Exceptions/Request/ConflictException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class ConflictException extends B2APIException {} diff --git a/src/Exceptions/Request/DownloadCapExceededException.php b/src/Exceptions/Request/DownloadCapExceededException.php index a59742e..a86109c 100644 --- a/src/Exceptions/Request/DownloadCapExceededException.php +++ b/src/Exceptions/Request/DownloadCapExceededException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class DownloadCapExceededException extends CapExceededException {} diff --git a/src/Exceptions/Request/DuplicateBucketNameException.php b/src/Exceptions/Request/DuplicateBucketNameException.php index 302db86..af0f39e 100644 --- a/src/Exceptions/Request/DuplicateBucketNameException.php +++ b/src/Exceptions/Request/DuplicateBucketNameException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class DuplicateBucketNameException extends B2APIException {} diff --git a/src/Exceptions/Request/ExpiredAuthTokenException.php b/src/Exceptions/Request/ExpiredAuthTokenException.php index 7e67e67..99479e9 100644 --- a/src/Exceptions/Request/ExpiredAuthTokenException.php +++ b/src/Exceptions/Request/ExpiredAuthTokenException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class ExpiredAuthTokenException extends B2APIException {} diff --git a/src/Exceptions/Request/FileNotPresentException.php b/src/Exceptions/Request/FileNotPresentException.php index 7d2812d..b142b63 100644 --- a/src/Exceptions/Request/FileNotPresentException.php +++ b/src/Exceptions/Request/FileNotPresentException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class FileNotPresentException extends B2APIException {} diff --git a/src/Exceptions/Request/InvalidBucketIdException.php b/src/Exceptions/Request/InvalidBucketIdException.php index 80cc347..1b89472 100644 --- a/src/Exceptions/Request/InvalidBucketIdException.php +++ b/src/Exceptions/Request/InvalidBucketIdException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class InvalidBucketIdException extends B2APIException {} diff --git a/src/Exceptions/Request/InvalidFileIdException.php b/src/Exceptions/Request/InvalidFileIdException.php index fd08bb2..3cc3007 100644 --- a/src/Exceptions/Request/InvalidFileIdException.php +++ b/src/Exceptions/Request/InvalidFileIdException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class InvalidFileIdException extends B2APIException {} diff --git a/src/Exceptions/Request/MethodNotAllowedException.php b/src/Exceptions/Request/MethodNotAllowedException.php index f1282b8..9e59577 100644 --- a/src/Exceptions/Request/MethodNotAllowedException.php +++ b/src/Exceptions/Request/MethodNotAllowedException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class MethodNotAllowedException extends B2APIException {} diff --git a/src/Exceptions/Request/NotFoundException.php b/src/Exceptions/Request/NotFoundException.php index 6b3501e..ddd69b8 100644 --- a/src/Exceptions/Request/NotFoundException.php +++ b/src/Exceptions/Request/NotFoundException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class NotFoundException extends B2APIException {} diff --git a/src/Exceptions/Request/OutOfRangeException.php b/src/Exceptions/Request/OutOfRangeException.php index 6d01571..8365d08 100644 --- a/src/Exceptions/Request/OutOfRangeException.php +++ b/src/Exceptions/Request/OutOfRangeException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class OutOfRangeException extends B2APIException {} diff --git a/src/Exceptions/Request/RangeNotSatisfiableException.php b/src/Exceptions/Request/RangeNotSatisfiableException.php index b009e51..6ae90ad 100644 --- a/src/Exceptions/Request/RangeNotSatisfiableException.php +++ b/src/Exceptions/Request/RangeNotSatisfiableException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class RangeNotSatisfiableException extends B2APIException {} diff --git a/src/Exceptions/Request/RequestTimeoutException.php b/src/Exceptions/Request/RequestTimeoutException.php index 6f5d3c0..6adbb47 100644 --- a/src/Exceptions/Request/RequestTimeoutException.php +++ b/src/Exceptions/Request/RequestTimeoutException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class RequestTimeoutException extends B2APIException {} diff --git a/src/Exceptions/Request/ServiceUnavailableException.php b/src/Exceptions/Request/ServiceUnavailableException.php index e73c557..697da32 100644 --- a/src/Exceptions/Request/ServiceUnavailableException.php +++ b/src/Exceptions/Request/ServiceUnavailableException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class ServiceUnavailableException extends B2APIException {} diff --git a/src/Exceptions/Request/StorageCapExceededException.php b/src/Exceptions/Request/StorageCapExceededException.php index 9553e05..7722f71 100644 --- a/src/Exceptions/Request/StorageCapExceededException.php +++ b/src/Exceptions/Request/StorageCapExceededException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class StorageCapExceededException extends CapExceededException {} diff --git a/src/Exceptions/Request/TooManyBucketsException.php b/src/Exceptions/Request/TooManyBucketsException.php index b3b256f..5144b8c 100644 --- a/src/Exceptions/Request/TooManyBucketsException.php +++ b/src/Exceptions/Request/TooManyBucketsException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class TooManyBucketsException extends B2APIException {} diff --git a/src/Exceptions/Request/TransactionCapExceededException.php b/src/Exceptions/Request/TransactionCapExceededException.php index dc4971e..6b13ce3 100644 --- a/src/Exceptions/Request/TransactionCapExceededException.php +++ b/src/Exceptions/Request/TransactionCapExceededException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class TransactionCapExceededException extends CapExceededException {} diff --git a/src/Exceptions/Request/UnauthorizedException.php b/src/Exceptions/Request/UnauthorizedException.php index 53cf256..64fa308 100644 --- a/src/Exceptions/Request/UnauthorizedException.php +++ b/src/Exceptions/Request/UnauthorizedException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class UnauthorizedException extends B2APIException {} diff --git a/src/Exceptions/Request/UnsupportedException.php b/src/Exceptions/Request/UnsupportedException.php index dd0693c..766c9b4 100644 --- a/src/Exceptions/Request/UnsupportedException.php +++ b/src/Exceptions/Request/UnsupportedException.php @@ -2,4 +2,5 @@ namespace Zaxbux\BackblazeB2\Exceptions\Request; +/** @package Zaxbux\BackblazeB2\Exceptions\Request */ class UnsupportedException extends B2APIException {} diff --git a/src/Helpers/AbstractHelper.php b/src/Helpers/AbstractHelper.php index 2ecf801..677771e 100644 --- a/src/Helpers/AbstractHelper.php +++ b/src/Helpers/AbstractHelper.php @@ -8,6 +8,7 @@ use Zaxbux\BackblazeB2\Client; use Zaxbux\BackblazeB2\Interfaces\HelperInterface; +/** @package Zaxbux\BackblazeB2\Helpers */ abstract class AbstractHelper implements HelperInterface { /** @var \Zaxbux\BackblazeB2\Client */ diff --git a/src/Helpers/AccountAuthorizationHelper.php b/src/Helpers/AccountAuthorizationHelper.php deleted file mode 100644 index d184b0d..0000000 --- a/src/Helpers/AccountAuthorizationHelper.php +++ /dev/null @@ -1,9 +0,0 @@ - Utils::isStream($sink), ]); - return FileDownload::create($response, !is_string($sink) ? null : $sink); + return FileDownload::fromResponse($response, !is_string($sink) ? null : $sink); } } \ No newline at end of file diff --git a/src/Helpers/FileBulkOperationsHelper.php b/src/Helpers/FileBulkOperationsHelper.php new file mode 100644 index 0000000..2be33d6 --- /dev/null +++ b/src/Helpers/FileBulkOperationsHelper.php @@ -0,0 +1,79 @@ +fileName = $fileName; + return $this; + } + + public function list(): Iterator + { + return $this->client->listAllFileVersions(null, null, null, $this->fileName); + } + + public function delete(?bool $bypassGovernance = false): FileList + { + return $this->client->deleteAllFileVersions(null, $this->fileName, null, null, null, $bypassGovernance); + } + + public function updateLegalHold(string $legalHold): FileList + { + return $this->apply(function(File $version) use ($legalHold) { + $this->client->updateFileLegalHold($version->getId(), $version->getName(), $legalHold); + }); + } + + public function updateRetention(array $fileRetention, ?bool $bypassGovernance = false): FileList + { + return $this->apply(function($version) use ($fileRetention, $bypassGovernance) { + $this->client->updateFileRetention( + $version->getId(), + $version->getName(), + $fileRetention, + $bypassGovernance + ); + }); + } + + /** + * Applies a callback to each file version. + * + * @param callable(File):FileList $operation + */ + public function apply(callable $operation): FileList + { + $fileVersions = $this->list(); + + $array = []; + + while ($fileVersions->valid()) { + $version = $fileVersions->current(); + + $array[] = $operation($version); + + $fileVersions->next(); + } + + return new FileList(new ArrayIterator($array)); + } +} \ No newline at end of file diff --git a/src/Helpers/LargeFileUpload.php b/src/Helpers/LargeFileUpload.php index 85dd508..e78ffb3 100644 --- a/src/Helpers/LargeFileUpload.php +++ b/src/Helpers/LargeFileUpload.php @@ -9,7 +9,7 @@ use Zaxbux\BackblazeB2\Object\File\FileUploadMetadata; use Zaxbux\BackblazeB2\Object\File\ServerSideEncryption; - +/** @package Zaxbux\BackblazeB2\Helpers */ class LargeFileUpload { private $client; private $stream; @@ -169,12 +169,12 @@ public function getFile() { private function minimumPartSize(): int { - return $this->accountAuthorization()->getAbsoluteMinimumPartSize(); + return $this->client->accountAuthorization()->getAbsoluteMinimumPartSize(); } private function recommendedPartSize(): int { - return $this->accountAuthorization()->getRecommendedPartSize(); + return $this->client->accountAuthorization()->getRecommendedPartSize(); } private function remainingBytes(): int diff --git a/src/Helpers/UploadHelper.php b/src/Helpers/UploadHelper.php index d8d1ab7..f951f99 100644 --- a/src/Helpers/UploadHelper.php +++ b/src/Helpers/UploadHelper.php @@ -11,6 +11,7 @@ use Zaxbux\BackblazeB2\Object\File\ServerSideEncryption; use Zaxbux\BackblazeB2\Object\File\UploadUrl; +/** @package Zaxbux\BackblazeB2\Helpers */ class UploadHelper extends AbstractHelper { /** * Upload a file. Automatically decides the upload method (regular or large file). diff --git a/tests/Endpoint.php b/src/Http/Endpoint.php similarity index 95% rename from tests/Endpoint.php rename to src/Http/Endpoint.php index 769d57d..4282c7b 100644 --- a/tests/Endpoint.php +++ b/src/Http/Endpoint.php @@ -1,7 +1,8 @@ [ + StatusCode::HTTP_BAD_REQUEST => [ 'bad_request' => BadRequestException::class, 'too_many_buckets' => TooManyBucketsException::class, 'duplicate_bucket_name' => DuplicateBucketNameException::class, @@ -46,26 +47,26 @@ class ErrorHandler 'bad_bucket_id' => InvalidBucketIdException::class, 'invalid_file_id' => InvalidFileIdException::class, ], - Response::HTTP_UNAUTHORIZED => [ + StatusCode::HTTP_UNAUTHORIZED => [ 'unsupported' => UnsupportedException::class, 'unauthorized' => UnauthorizedException::class, 'bad_auth_token' => BadAuthTokenException::class, 'expired_auth_token' => ExpiredAuthTokenException::class, 'access_denied' => AccessDeniedException::class, ], - Response::HTTP_FORBIDDEN => [ + StatusCode::HTTP_FORBIDDEN => [ 'cap_exceeded' => CapExceededException::class, 'storage_cap_exceeded' => StorageCapExceededException::class, 'transaction_cap_exceeded' => TransactionCapExceededException::class, 'access_denied' => AccessDeniedException::class, 'download_cap_exceeded' => DownloadCapExceededException::class, ], - Response::HTTP_NOT_FOUND => ['not_found' => NotFoundException::class,], - Response::HTTP_METHOD_NOT_ALLOWED => ['method_not_allowed' => MethodNotAllowedException::class,], - Response::HTTP_REQUEST_TIMEOUT => ['request_timeout' => RequestTimeoutException::class,], - Response::HTTP_CONFLICT => ['conflict' => ConflictException::class,], - Response::HTTP_RANGE_NOT_SATISFIABLE => ['range_not_satisfiable' => RangeNotSatisfiableException::class,], - Response::HTTP_SERVICE_UNAVAILABLE => [ + StatusCode::HTTP_NOT_FOUND => ['not_found' => NotFoundException::class,], + StatusCode::HTTP_METHOD_NOT_ALLOWED => ['method_not_allowed' => MethodNotAllowedException::class,], + StatusCode::HTTP_REQUEST_TIMEOUT => ['request_timeout' => RequestTimeoutException::class,], + StatusCode::HTTP_CONFLICT => ['conflict' => ConflictException::class,], + StatusCode::HTTP_RANGE_NOT_SATISFIABLE => ['range_not_satisfiable' => RangeNotSatisfiableException::class,], + StatusCode::HTTP_SERVICE_UNAVAILABLE => [ 'service_unavailable' => ServiceUnavailableException::class, 'bad_request' => BadRequestException::class, ], diff --git a/src/Http/Exceptions/TooManyRequestsException.php b/src/Http/Exceptions/TooManyRequestsException.php index 4b1f482..4e3457a 100644 --- a/src/Http/Exceptions/TooManyRequestsException.php +++ b/src/Http/Exceptions/TooManyRequestsException.php @@ -2,6 +2,5 @@ namespace Zaxbux\BackblazeB2\Http\Exceptions; -class TooManyRequestsException extends \GuzzleHttp\Exception\RequestException -{ -} +/** @package Zaxbux\BackblazeB2\Http\Exceptions */ +class TooManyRequestsException extends \GuzzleHttp\Exception\RequestException { } diff --git a/src/Http/Middleware/ApplyAuthorizationMiddleware.php b/src/Http/Middleware/ApplyAuthorizationMiddleware.php index 9074de7..c470aec 100644 --- a/src/Http/Middleware/ApplyAuthorizationMiddleware.php +++ b/src/Http/Middleware/ApplyAuthorizationMiddleware.php @@ -8,6 +8,7 @@ use Zaxbux\BackblazeB2\Client; use Zaxbux\BackblazeB2\Utils; +/** @package Zaxbux\BackblazeB2\Http\Middleware */ class ApplyAuthorizationMiddleware { private $client; diff --git a/src/Http/Middleware/ExceptionMiddleware.php b/src/Http/Middleware/ExceptionMiddleware.php index 4a89482..0a0cfc8 100644 --- a/src/Http/Middleware/ExceptionMiddleware.php +++ b/src/Http/Middleware/ExceptionMiddleware.php @@ -6,8 +6,9 @@ use Psr\Http\Message\ResponseInterface; use Zaxbux\BackblazeB2\Http\ErrorHandler; use Zaxbux\BackblazeB2\Http\Exceptions\TooManyRequestsException; -use Zaxbux\BackblazeB2\Http\Response; +use Zaxbux\BackblazeB2\Http\StatusCode; +/** @package Zaxbux\BackblazeB2\Http\Middleware */ class ExceptionMiddleware { public function __invoke(callable $handler) @@ -20,7 +21,7 @@ public function __invoke(callable $handler) return $response; } - if ($response->getStatusCode() === Response::HTTP_TOO_MANY_REQUESTS) { + if ($response->getStatusCode() === StatusCode::HTTP_TOO_MANY_REQUESTS) { throw new TooManyRequestsException('', $request, $response); } @@ -31,6 +32,6 @@ public function __invoke(callable $handler) public static function isSuccessful(ResponseInterface $response) { - return $response->getStatusCode() >= Response::HTTP_OK && $response->getStatusCode() <= Response::HTTP_PARTIAL_CONTENT; + return $response->getStatusCode() >= StatusCode::HTTP_OK && $response->getStatusCode() <= StatusCode::HTTP_PARTIAL_CONTENT; } } diff --git a/src/Http/Middleware/RetryMiddleware.php b/src/Http/Middleware/RetryMiddleware.php index ee9c629..8429327 100644 --- a/src/Http/Middleware/RetryMiddleware.php +++ b/src/Http/Middleware/RetryMiddleware.php @@ -7,13 +7,14 @@ use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Zaxbux\BackblazeB2\Config; -use Zaxbux\BackblazeB2\Http\Response; +use Zaxbux\BackblazeB2\Http\StatusCode; +/** @package Zaxbux\BackblazeB2\Http\Middleware */ class RetryMiddleware { protected const RETRY_STATUS_CODES = [ - Response::HTTP_TOO_MANY_REQUESTS, - Response::HTTP_SERVICE_UNAVAILABLE + StatusCode::HTTP_TOO_MANY_REQUESTS, + StatusCode::HTTP_SERVICE_UNAVAILABLE ]; /** @var \Zaxbux\BackblazeB2\Config */ @@ -71,7 +72,7 @@ private static function retryDecider(Config $config): callable { private static function retryDelay(Config $config): callable { return static function(int $retries, ResponseInterface $response) use ($config): int { // Use the value of the `Retry-After` header - if ($response->getStatusCode() === Response::HTTP_TOO_MANY_REQUESTS) { + if ($response->getStatusCode() === StatusCode::HTTP_TOO_MANY_REQUESTS) { $delay = (int) $response->getHeader('Retry-After')[0] ?? $config->maxRetryDelay(); return $delay * 1000; } diff --git a/src/Http/Response.php b/src/Http/Response.php index 1a550fe..0797c29 100644 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -2,7 +2,8 @@ namespace Zaxbux\BackblazeB2\Http; -final class Response +/** @package Zaxbux\BackblazeB2\Http */ +final class StatusCode { public const HTTP_CONTINUE = 100; public const HTTP_SWITCHING_PROTOCOLS = 101; diff --git a/src/Interfaces/AuthorizationCacheInterface.php b/src/Interfaces/AuthorizationCacheInterface.php index fe23301..dec613a 100644 --- a/src/Interfaces/AuthorizationCacheInterface.php +++ b/src/Interfaces/AuthorizationCacheInterface.php @@ -7,6 +7,7 @@ use Zaxbux\BackblazeB2\Object\AccountAuthorization; +/** @package Zaxbux\BackblazeB2\Interfaces */ interface AuthorizationCacheInterface { diff --git a/src/Interfaces/B2ObjectInterface.php b/src/Interfaces/B2ObjectInterface.php index 6911b10..ccb3f55 100644 --- a/src/Interfaces/B2ObjectInterface.php +++ b/src/Interfaces/B2ObjectInterface.php @@ -7,6 +7,7 @@ use ArrayAccess; use JsonSerializable; +/** @package Zaxbux\BackblazeB2\Interfaces */ interface B2ObjectInterface extends JsonSerializable, ArrayAccess { public static function fromArray(array $data): B2ObjectInterface; diff --git a/src/Interfaces/HelperInterface.php b/src/Interfaces/HelperInterface.php index 349efcb..5140403 100644 --- a/src/Interfaces/HelperInterface.php +++ b/src/Interfaces/HelperInterface.php @@ -6,6 +6,7 @@ use Zaxbux\BackblazeB2\Client; +/** @package Zaxbux\BackblazeB2\Interfaces */ interface HelperInterface { public function __construct(Client $client); diff --git a/src/Object/AccountAuthorization.php b/src/Object/AccountAuthorization.php index 6655e80..ade0389 100644 --- a/src/Object/AccountAuthorization.php +++ b/src/Object/AccountAuthorization.php @@ -5,18 +5,14 @@ namespace Zaxbux\BackblazeB2\Object; use function time; -use function json_encode; -use GuzzleHttp\ClientInterface; use GuzzleHttp\Utils; use Psr\Http\Message\ResponseInterface; -use Zaxbux\BackblazeB2\Client; -use Zaxbux\BackblazeB2\Config; use Zaxbux\BackblazeB2\Interfaces\B2ObjectInterface; use Zaxbux\BackblazeB2\Interfaces\AuthorizationCacheInterface; use Zaxbux\BackblazeB2\Traits\ProxyArrayAccessToPropertiesTrait; - +/** @package Zaxbux\BackblazeB2\Object */ class AccountAuthorization implements B2ObjectInterface { use ProxyArrayAccessToPropertiesTrait; @@ -33,7 +29,7 @@ class AccountAuthorization implements B2ObjectInterface public const ATTRIBUTE_RECOMMENDED_PART_SIZE = 'recommendedPartSize'; public const ATTRIBUTE_S3_API_URL = 's3ApiUrl'; - /** @deprecated */ + /** @deprecated Removed in B2 API v2 */ public const ATTRIBUTE_MINIMUM_PART_SIZE = 'minimumPartSize'; /** @var string */ @@ -103,8 +99,10 @@ public function getAuthorizationToken(): ?string /** * Get the capabilities, bucket restrictions, and prefix restrictions. + * + * @return null|string|array */ - public function getAllowed(?string $key = null): ?array + public function getAllowed(?string $key = null) { if ($key) { return $this->allowed[$key] ?? null; diff --git a/src/Object/Bucket.php b/src/Object/Bucket.php index 744d7a7..c02071d 100644 --- a/src/Object/Bucket.php +++ b/src/Object/Bucket.php @@ -9,6 +9,7 @@ //use Zaxbux\BackblazeB2\Traits\IterableFromArrayTrait; use Zaxbux\BackblazeB2\Traits\ProxyArrayAccessToPropertiesTrait; +/** @package Zaxbux\BackblazeB2\Object */ class Bucket implements B2ObjectInterface { use ProxyArrayAccessToPropertiesTrait; diff --git a/src/Object/Bucket/BucketInfo.php b/src/Object/Bucket/BucketInfo.php index 8a5f9bb..f22f8fe 100644 --- a/src/Object/Bucket/BucketInfo.php +++ b/src/Object/Bucket/BucketInfo.php @@ -9,7 +9,7 @@ /** * @link https://www.backblaze.com/b2/docs/buckets.html#bucketInfo * - + * @package Zaxbux\BackblazeB2\Object\Bucket */ final class BucketInfo extends AbstractObjectInfo { public static function fromArray(array $data): BucketInfo { diff --git a/src/Object/Bucket/BucketType.php b/src/Object/Bucket/BucketType.php index 289e942..3e87a43 100644 --- a/src/Object/Bucket/BucketType.php +++ b/src/Object/Bucket/BucketType.php @@ -4,6 +4,7 @@ namespace Zaxbux\BackblazeB2\Object\Bucket; +/** @package Zaxbux\BackblazeB2\Object\Bucket */ final class BucketType { public const ALL = 'all'; diff --git a/src/Object/Bucket/CORSRule.php b/src/Object/Bucket/CORSRule.php index c5ffe49..fdba15d 100644 --- a/src/Object/Bucket/CORSRule.php +++ b/src/Object/Bucket/CORSRule.php @@ -5,7 +5,9 @@ use JsonSerializable; /** - * A CORS rule object for the Backblaze B2 API + * A CORS rule object. + * + * @package Zaxbux\BackblazeB2\Object\Bucket */ class CORSRule implements JsonSerializable { diff --git a/src/Object/Bucket/LifecycleRule.php b/src/Object/Bucket/LifecycleRule.php index 16b9304..976a671 100644 --- a/src/Object/Bucket/LifecycleRule.php +++ b/src/Object/Bucket/LifecycleRule.php @@ -7,7 +7,8 @@ use JsonSerializable; /** - * A Lifecycle rule object for the Backblaze B2 API + * A Lifecycle rule object. + * @package Zaxbux\BackblazeB2\Object\Bucket */ class LifecycleRule implements JsonSerializable { diff --git a/src/Object/DownloadAuthorization.php b/src/Object/DownloadAuthorization.php index d46f6d7..6031ff9 100644 --- a/src/Object/DownloadAuthorization.php +++ b/src/Object/DownloadAuthorization.php @@ -7,6 +7,7 @@ use Zaxbux\BackblazeB2\Interfaces\B2ObjectInterface; use Zaxbux\BackblazeB2\Traits\ProxyArrayAccessToPropertiesTrait; +/** @package Zaxbux\BackblazeB2\Object */ class DownloadAuthorization implements B2ObjectInterface { use ProxyArrayAccessToPropertiesTrait; diff --git a/src/Object/File.php b/src/Object/File.php index 6c80428..7703835 100644 --- a/src/Object/File.php +++ b/src/Object/File.php @@ -12,6 +12,7 @@ use Zaxbux\BackblazeB2\Traits\ProxyArrayAccessToPropertiesTrait; +/** @package Zaxbux\BackblazeB2\Object */ class File implements B2ObjectInterface { use ProxyArrayAccessToPropertiesTrait; @@ -135,7 +136,7 @@ class File implements B2ObjectInterface * @param int $uploadTimestamp * @param string $accountId * @param array $retention - * @param array $legalHold + * @param string|array $legalHold * @param array $serverSideEncryption * @param int $partNumber */ @@ -152,7 +153,7 @@ public function __construct( ?int $uploadTimestamp = null, ?string $accountId = null, ?array $retention = null, - ?array $legalHold = null, + $legalHold = null, ?array $serverSideEncryption = null, ?int $partNumber = null ) { @@ -170,7 +171,7 @@ public function __construct( $this->uploadTimestamp = $uploadTimestamp; $this->accountId = $accountId; $this->retention = $retention; - $this->legalHold = $legalHold; + $this->legalHold = is_array($legalHold) ? $legalHold : ['value' => $legalHold]; $this->serverSideEncryption = $serverSideEncryption instanceof ServerSideEncryption ? $serverSideEncryption : ServerSideEncryption::fromArray($serverSideEncryption ?? []); $this->partNumber = $partNumber; diff --git a/src/Object/File/DownloadOptions.php b/src/Object/File/DownloadOptions.php index 5adf401..feeb6ea 100644 --- a/src/Object/File/DownloadOptions.php +++ b/src/Object/File/DownloadOptions.php @@ -13,6 +13,7 @@ use Zaxbux\BackblazeB2\Object\File\ServerSideEncryption; use Zaxbux\BackblazeB2\Traits\ProxyArrayAccessToPropertiesTrait; +/** @package Zaxbux\BackblazeB2\Object\File */ final class DownloadOptions implements ArrayAccess, JsonSerializable { use ProxyArrayAccessToPropertiesTrait; diff --git a/src/Object/File/FileActionType.php b/src/Object/File/FileActionType.php index b425465..650da89 100644 --- a/src/Object/File/FileActionType.php +++ b/src/Object/File/FileActionType.php @@ -6,6 +6,7 @@ use InvalidArgumentException; +/** @package Zaxbux\BackblazeB2\Object\File */ final class FileActionType { /** diff --git a/src/Object/File/FileInfo.php b/src/Object/File/FileInfo.php index 47dc11c..de903e4 100644 --- a/src/Object/File/FileInfo.php +++ b/src/Object/File/FileInfo.php @@ -10,7 +10,7 @@ /** * @link https://www.backblaze.com/b2/docs/files.html#fileInfo * - + * @package Zaxbux\BackblazeB2\Object\File */ final class FileInfo extends AbstractObjectInfo { public const B2_FILE_INFO_MTIME = 'src_last_modified_millis'; diff --git a/src/Object/File/FileLock.php b/src/Object/File/FileLock.php index 1dac57f..737d934 100644 --- a/src/Object/File/FileLock.php +++ b/src/Object/File/FileLock.php @@ -11,7 +11,8 @@ /** * @link https://www.backblaze.com/b2/docs/server_side_encryption.html - + * + * @package Zaxbux\BackblazeB2\Object\File */ class FileLock implements JsonSerializable, ArrayAccess { use ProxyArrayAccessToPropertiesTrait; diff --git a/src/Object/File/FileUploadMetadata.php b/src/Object/File/FileUploadMetadata.php index c5023b0..2a43447 100644 --- a/src/Object/File/FileUploadMetadata.php +++ b/src/Object/File/FileUploadMetadata.php @@ -10,7 +10,7 @@ /** * Calculate the length and hash of a string or stream. * - + * @package Zaxbux\BackblazeB2\Object\File */ final class FileUploadMetadata { diff --git a/src/Object/File/ServerSideEncryption.php b/src/Object/File/ServerSideEncryption.php index 34ed015..0322624 100644 --- a/src/Object/File/ServerSideEncryption.php +++ b/src/Object/File/ServerSideEncryption.php @@ -15,7 +15,7 @@ /** * @link https://www.backblaze.com/b2/docs/server_side_encryption.html - + * @package Zaxbux\BackblazeB2\Object\File */ class ServerSideEncryption implements JsonSerializable, ArrayAccess { use ProxyArrayAccessToPropertiesTrait; diff --git a/src/Object/File/UploadPartUrl.php b/src/Object/File/UploadPartUrl.php index 4a9d53f..cdd0fde 100644 --- a/src/Object/File/UploadPartUrl.php +++ b/src/Object/File/UploadPartUrl.php @@ -4,6 +4,7 @@ namespace Zaxbux\BackblazeB2\Object\File; +/** @package Zaxbux\BackblazeB2\Object\File */ class UploadPartUrl { public const ATTRIBUTE_FILE_ID = 'fileId'; public const ATTRIBUTE_UPLOAD_URL = 'uploadUrl'; diff --git a/src/Object/File/UploadUrl.php b/src/Object/File/UploadUrl.php index 7abb553..af54c16 100644 --- a/src/Object/File/UploadUrl.php +++ b/src/Object/File/UploadUrl.php @@ -4,6 +4,7 @@ namespace Zaxbux\BackblazeB2\Object\File; +/** @package Zaxbux\BackblazeB2\Object\File */ class UploadUrl { public const ATTRIBUTE_BUCKET_ID = 'bucketId'; public const ATTRIBUTE_UPLOAD_URL = 'uploadUrl'; diff --git a/src/Object/Key.php b/src/Object/Key.php index 9cd4190..7cb3998 100644 --- a/src/Object/Key.php +++ b/src/Object/Key.php @@ -7,6 +7,7 @@ use Zaxbux\BackblazeB2\Interfaces\B2ObjectInterface; use Zaxbux\BackblazeB2\Traits\ProxyArrayAccessToPropertiesTrait; +/** @package Zaxbux\BackblazeB2\Object */ class Key implements B2ObjectInterface { use ProxyArrayAccessToPropertiesTrait; diff --git a/src/Object/Key/CapabilityType.php b/src/Object/Key/CapabilityType.php index e577d02..10bb5ea 100644 --- a/src/Object/Key/CapabilityType.php +++ b/src/Object/Key/CapabilityType.php @@ -2,6 +2,7 @@ namespace Zaxbux\BackblazeB2\Object\Key; +/** @package Zaxbux\BackblazeB2\Object\Key */ final class CapabilityType { public const BYPASS_GOVERNANCE = 'bypassGovernance'; public const DELETE_BUCKETS = 'deleteBuckets'; diff --git a/src/Operations/ApplicationKeyOperationsTrait.php b/src/Operations/ApplicationKeyOperationsTrait.php index 666a660..d648dc9 100644 --- a/src/Operations/ApplicationKeyOperationsTrait.php +++ b/src/Operations/ApplicationKeyOperationsTrait.php @@ -6,11 +6,13 @@ use AppendIterator; use NoRewindIterator; +use Zaxbux\BackblazeB2\Http\Endpoint; use Zaxbux\BackblazeB2\Object\AccountAuthorization; use Zaxbux\BackblazeB2\Object\Key; use Zaxbux\BackblazeB2\Response\KeyList; use Zaxbux\BackblazeB2\Utils; +/** @package Zaxbux\BackblazeB2\Operations */ trait ApplicationKeyOperationsTrait { @@ -24,6 +26,9 @@ abstract protected function accountAuthorization(): AccountAuthorization; * * @link https://www.backblaze.com/b2/docs/b2_create_key.html * + * @b2-capability writeKeys + * @b2-transaction Class C + * * @param string $keyName A name for this key. There is no requirement that the name be unique. * The name cannot be used to look up the key. * @param string[] $capabilities A list of strings, each one naming a capability the new key should have. @@ -39,7 +44,7 @@ public function createKey( ?string $bucketId = null, ?string $namePrefix = null ): Key { - $response = $this->http->request('POST', 'b2_create_key', [ + $response = $this->http->request('POST', Endpoint::CREATE_KEY, [ 'json' => Utils::filterRequestOptions([ Key::ATTRIBUTE_ACCOUNT_ID => $this->accountAuthorization()->getAccountId(), Key::ATTRIBUTE_CAPABILITIES => $capabilities, @@ -51,25 +56,28 @@ public function createKey( ]), ]); - return Key::fromArray(json_decode((string) $response->getBody(), true)); + return Key::fromArray(Utils::jsonDecode($response)); } /** * Deletes the application key specified. * * @link https://www.backblaze.com/b2/docs/b2_delete_key.html + * + * @b2-capability deleteKeys + * @b2-transaction Class A * * @param string $applicationKeyId The key to delete. */ public function deleteKey(string $applicationKeyId): Key { - $response = $this->http->request('POST', 'b2_delete_key', [ + $response = $this->http->request('POST', Endpoint::DELETE_KEY, [ 'json' => [ Key::ATTRIBUTE_APPLICATION_KEY_ID => $applicationKeyId, ] ]); - return Key::fromArray(json_decode((string) $response->getBody(), true)); + return Key::fromArray(Utils::jsonDecode($response)); } /** @@ -77,6 +85,9 @@ public function deleteKey(string $applicationKeyId): Key * * @link https://www.backblaze.com/b2/docs/b2_list_keys.html * + * @b2-capability listKeys + * @b2-transaction Class C + * * @param string $startApplicationKeyId The first key to return. * @param int $maxKeyCount The maximum number of keys to return in the response. The default value is * 1000, and the maximum is 10000. The maximum number of keys returned per @@ -86,7 +97,7 @@ public function listKeys( ?string $startApplicationKeyId = null, ?int $maxKeyCount = 1000 ): KeyList { - $response = $this->http->request('POST', 'b2_list_keys', [ + $response = $this->http->request('POST', Endpoint::LIST_KEYS, [ 'json' => Utils::filterRequestOptions([ Key::ATTRIBUTE_ACCOUNT_ID => $this->accountAuthorization()->getAccountId(), ], [ @@ -95,7 +106,7 @@ public function listKeys( ]), ]); - return KeyList::create($response); + return KeyList::fromResponse($response); } /** diff --git a/src/Operations/BucketOperationsTrait.php b/src/Operations/BucketOperationsTrait.php index a02b907..1a17f2b 100644 --- a/src/Operations/BucketOperationsTrait.php +++ b/src/Operations/BucketOperationsTrait.php @@ -5,6 +5,7 @@ namespace Zaxbux\BackblazeB2\Operations; use Zaxbux\BackblazeB2\Exceptions\NotFoundException; +use Zaxbux\BackblazeB2\Http\Endpoint; use Zaxbux\BackblazeB2\Object\AccountAuthorization; use Zaxbux\BackblazeB2\Object\Bucket; use Zaxbux\BackblazeB2\Object\Bucket\BucketInfo; @@ -12,6 +13,7 @@ use Zaxbux\BackblazeB2\Object\Bucket\BucketType; use Zaxbux\BackblazeB2\Utils; +/** @package Zaxbux\BackblazeB2\Operations */ trait BucketOperationsTrait { @@ -24,6 +26,9 @@ abstract protected function accountAuthorization(): AccountAuthorization; * Create a bucket with the given name and type. * * @link https://www.backblaze.com/b2/docs/b2_create_bucket.html + * + * @b2-capability writeBuckets + * @b2-transaction Class C * * @param string $bucketName The name to give the new bucket. * @param string $bucketType Either "allPublic", meaning that files in this bucket can be downloaded @@ -36,11 +41,11 @@ abstract protected function accountAuthorization(): AccountAuthorization; public function createBucket( string $bucketName, ?string $bucketType = BucketType::PRIVATE, - $bucketInfo = null, + $bucketInfo = null, ?array $corsRules = null, ?array $lifecycleRules = null ): Bucket { - $response = $this->http->request('POST', 'b2_create_bucket', [ + $response = $this->http->request('POST', Endpoint::CREATE_BUCKET, [ 'json' => Utils::filterRequestOptions([ Bucket::ATTRIBUTE_ACCOUNT_ID => $this->accountAuthorization()->getAccountId(), Bucket::ATTRIBUTE_BUCKET_NAME => $bucketName, @@ -52,13 +57,16 @@ public function createBucket( ]), ]); - return Bucket::fromArray(json_decode((string) $response->getBody(), true)); + return Bucket::fromArray(Utils::jsonDecode($response)); } /** * Deletes the bucket specified. Only buckets that contain no version of any files can be deleted. * - * @link https://www.backblaze.com/b2/docs/b2_delete_bucket.html + * @link https://www.backblaze.com/b2/docs/b2_delete_bucket.html + * + * @b2-capability deleteBuckets + * @b2-transaction Class A * * @param string $bucketId The ID of the bucket to delete. * @param bool $withFiles Delete all file versions first. @@ -70,20 +78,23 @@ public function deleteBucket(string $bucketId, ?bool $withFiles = false): Bucket $this->deleteAllFileVersions($bucketId); } - $response = $this->http->request('POST', 'b2_delete_bucket', [ + $response = $this->http->request('POST', Endpoint::DELETE_BUCKET, [ 'json' => [ Bucket::ATTRIBUTE_ACCOUNT_ID => $this->accountAuthorization()->getAccountId(), Bucket::ATTRIBUTE_BUCKET_ID => $bucketId ] ]); - return Bucket::fromArray(json_decode((string) $response->getBody(), true)); + return Bucket::fromArray(Utils::jsonDecode($response)); } /** * Returns a list of bucket objects representing the buckets on the account. * * @link https://www.backblaze.com/b2/docs/b2_list_buckets.html + * + * @b2-capability listBuckets + * @b2-transaction Class C * * @param string $bucketId When bucketId is specified, the result will be a list containing just this bucket, * if it's present in the account, or no buckets if the account does not have a @@ -99,7 +110,7 @@ public function listBuckets( ?string $bucketName = null, ?array $bucketTypes = null ): BucketList { - $response = $this->http->request('POST', 'b2_list_buckets', [ + $response = $this->http->request('POST', Endpoint::LIST_BUCKETS, [ 'json' => Utils::filterRequestOptions([ Bucket::ATTRIBUTE_ACCOUNT_ID => $this->accountAuthorization()->getAccountId(), ], [ @@ -109,13 +120,16 @@ public function listBuckets( ]), ]); - return BucketList::create($response); + return BucketList::fromResponse($response); } /** * Updates the type attribute of a bucket by the given ID. * * @link https://www.backblaze.com/b2/docs/b2_update_bucket.html + * + * @b2-capability writeBuckets + * @b2-transaction Class C * * @param string $bucketId The unique ID of the bucket. * @param string $bucketType Either "allPublic", meaning that files in this bucket can be downloaded @@ -135,7 +149,7 @@ public function updateBucket( ?array $lifecycleRules = null, ?int $ifRevisionIs = null ): Bucket { - $response = $this->http->request('POST', 'b2_update_bucket', [ + $response = $this->http->request('POST', Endpoint::UPDATE_BUCKET, [ 'json' => Utils::filterRequestOptions([ Bucket::ATTRIBUTE_ACCOUNT_ID => $this->accountAuthorization()->getAccountId(), Bucket::ATTRIBUTE_BUCKET_ID => $bucketId ?? $this->getAllowedBucketId(), @@ -148,7 +162,7 @@ public function updateBucket( ]) ]); - return Bucket::fromArray(json_decode((string) $response->getBody(), true)); + return Bucket::fromArray(Utils::jsonDecode($response)); } /** @@ -165,11 +179,13 @@ public function getBucketById( ): Bucket { $response = $this->listBuckets($bucketId, null, $bucketTypes); - if (iterator_count($response->getBuckets()) !== 1) { + $buckets = $response->getBucketsArray(); + + if (count($buckets) !== 1) { throw new NotFoundException(sprintf('Bucket "%s" not found.', $bucketId)); } - return $response->getBuckets()[0]; + return $buckets[0]; } /** @@ -184,10 +200,12 @@ public function getBucketByName(string $bucketName, array $bucketTypes = null): { $response = $this->listBuckets(null, $bucketName, $bucketTypes); - if (iterator_count($response->getBuckets()) !== 1) { + $buckets = $response->getBucketsArray(); + + if (count($buckets) !== 1) { throw new NotFoundException(sprintf('Bucket "%s" not found.', $bucketName)); } - return $response->getBuckets()[0]; + return $buckets[0]; } } diff --git a/src/Operations/DownloadOperationsTrait.php b/src/Operations/DownloadOperationsTrait.php new file mode 100644 index 0000000..d77fa07 --- /dev/null +++ b/src/Operations/DownloadOperationsTrait.php @@ -0,0 +1,125 @@ +http->request('POST', Endpoint::GET_DOWNLOAD_AUTHORIZATION, [ + 'json' => Utils::filterRequestOptions([ + File::ATTRIBUTE_BUCKET_ID => $bucketId ?? $this->getAllowedBucketId(), + File::ATTRIBUTE_FILE_NAME_PREFIX => $fileNamePrefix, + File::ATTRIBUTE_VALID_DURATION => $validDuration, + ], $options->getAuthorizationOptions()), + ]); + + return DownloadAuthorization::fromArray(Utils::jsonDecode($response)); + } + + /** + * Downloads one file from B2 by File ID. + * + * @link https://www.backblaze.com/b2/docs/b2_download_file_by_id.html + * + * @b2-capability [readFiles] If the bucket is private. + * @b2-transaction Class B + * + * @param string $fileId The file ID to download. + * @param DownloadOptions|array $options An optional array of additional B2 API options. + * @param string $range A standard RFC 7233 byte-range request, that will only return part of the stored file. + * @param string|resource $sink A string, stream, or `StreamInterface` that specifies where to save the file. + * {@link https://docs.guzzlephp.org/en/stable/request-options.html#sink} + * @param bool $headersOnly Only get the file headers, without downloading the whole file. + */ + public function downloadFileById( + string $fileId, + $options = null, + $sink = null, + ?bool $headersOnly = false + ): FileDownload { + return DownloadHelper::instance($this)->download( + Utils::joinPaths( + $this->accountAuthorization()->getDownloadUrl(), + Client::B2_API_VERSION, + Endpoint::DOWNLOAD_FILE_BY_ID + ), + [File::ATTRIBUTE_FILE_ID => $fileId], + $options, + $sink, + $headersOnly + ); + } + + /** + * Downloads one file from B2 by File Name. + * + * @b2-capability [readFiles] If the bucket is private. + * @b2-transaction Class B + * + * @link https://www.backblaze.com/b2/docs/b2_download_file_by_name.html + * + * @param string $fileName The file name to download. + * @param string $bucketName The bucket the file is contained in. + * @param DownloadOptions|array $options An optional array of additional B2 API options. + * @param string $range A standard RFC 7233 byte-range request, that will only return part of the stored file. + * @param string|resource $sink A string, stream, or `StreamInterface` that specifies where to save the file. + * {@link https://docs.guzzlephp.org/en/stable/request-options.html#sink} + * @param bool $headersOnly Only get the file headers, without downloading the whole file. + */ + public function downloadFileByName( + string $fileName, + ?string $bucketName = null, + $options = null, + $sink = null, + ?bool $headersOnly = false + ): FileDownload { + return DownloadHelper::instance($this)->download( + Utils::joinPaths( + $this->accountAuthorization()->getApiUrl(), + 'file', + $bucketName ?? $this->getAllowedBucketName(), + $fileName + ), + null, + $options, + $sink, + $headersOnly + ); + } +} \ No newline at end of file diff --git a/src/Operations/FileOperationsTrait.php b/src/Operations/FileOperationsTrait.php index c2fc06b..b586123 100644 --- a/src/Operations/FileOperationsTrait.php +++ b/src/Operations/FileOperationsTrait.php @@ -8,23 +8,15 @@ use ArrayIterator; use Iterator; use NoRewindIterator; -use Zaxbux\BackblazeB2\Client; use Zaxbux\BackblazeB2\Exceptions\NotFoundException; -use Zaxbux\BackblazeB2\Helpers\DownloadHelper; +use Zaxbux\BackblazeB2\Http\Endpoint; use Zaxbux\BackblazeB2\Object\AccountAuthorization; use Zaxbux\BackblazeB2\Object\File; -use Zaxbux\BackblazeB2\Object\DownloadAuthorization; -use Zaxbux\BackblazeB2\Object\File\DownloadOptions; -use Zaxbux\BackblazeB2\Object\File\FileUploadMetadata; -use Zaxbux\BackblazeB2\Object\File\FileInfo; use Zaxbux\BackblazeB2\Object\File\ServerSideEncryption; -use Zaxbux\BackblazeB2\Object\File\UploadPartUrl; -use Zaxbux\BackblazeB2\Object\File\UploadUrl; -use Zaxbux\BackblazeB2\Response\FileDownload; use Zaxbux\BackblazeB2\Response\FileList; -use Zaxbux\BackblazeB2\Response\FilePartList; use Zaxbux\BackblazeB2\Utils; +/** @package Zaxbux\BackblazeB2\Operations */ trait FileOperationsTrait { @@ -33,29 +25,15 @@ trait FileOperationsTrait abstract protected function accountAuthorization(): AccountAuthorization; - /** - * Cancel the upload of a large file, and deletes all of the parts that have been uploaded. - * - * @link https://www.backblaze.com/b2/docs/b2_cancel_large_file.html - * - * @param string $fileId The ID returned by `b2_start_large_file`. - */ - public function cancelLargeFile(string $fileId) - { - $response = $this->http->request('POST', 'b2_cancel_large_file', [ - 'json' => [ - File::ATTRIBUTE_FILE_ID => $fileId, - ], - ]); - - return File::fromArray(json_decode((string) $response->getBody(), true)); - } - /** * Creates a new file by copying from an existing file. * * @link https://www.backblaze.com/b2/docs/b2_copy_file.html * + * @b2-capability writeFiles + * @b2-capability [readFiles] If the bucket is private. + * @b2-transaction Class C + * * @param string $sourceFileId The ID of the source file being copied. * @param string $fileName The name of the new file being created. * @param string $destinationBucketId The ID of the bucket where the copied file will be stored. @@ -84,34 +62,7 @@ public function copyFile( ?ServerSideEncryption $sourceSSE = null, ?ServerSideEncryption $destinationSSE = null ): File { - - /* - if ($metadataDirective) { - if ($metadataDirective == File::METADATA_DIRECTIVE_REPLACE && $contentType == null) { - $contentType = File::CONTENT_TYPE_AUTO; - } - - if ($contentType && $metadataDirective !== File::METADATA_DIRECTIVE_REPLACE) { - throw new InvalidArgumentException(sprintf( - '%s must not be set when %s is not "%s".', - File::ATTRIBUTE_CONTENT_TYPE, - File::ATTRIBUTE_METADATA_DIRECTIVE, - File::METADATA_DIRECTIVE_REPLACE - )); - } - - if ($fileInfo && $metadataDirective !== File::METADATA_DIRECTIVE_REPLACE) { - throw new InvalidArgumentException(sprintf( - '%s must not be set when %s is not "%s".', - File::ATTRIBUTE_FILE_INFO, - File::ATTRIBUTE_METADATA_DIRECTIVE, - File::METADATA_DIRECTIVE_REPLACE - )); - } - } - */ - - $response = $this->http->request('POST', 'b2_copy_file', [ + $response = $this->http->request('POST', Endpoint::COPY_FILE, [ 'json' => Utils::filterRequestOptions([ File::ATTRIBUTE_SOURCE_FILE_ID => $sourceFileId, File::ATTRIBUTE_FILE_NAME => $fileName, @@ -128,247 +79,58 @@ public function copyFile( ]) ]); - return File::fromArray(json_decode((string) $response->getBody(), true)); - } - - /** - * Copies from an existing B2 file, storing it as a part of a large file which has already been started with - * `b2_start_large_file`. - * - * @link https://www.backblaze.com/b2/docs/b2_copy_part.html - * - * @param string $sourceFileId The ID of the source file being copied. - * @param string $largeFileId The ID of the large file the part will belong to. - * @param int $partNumber A number from `1` to `10000`. The parts uploaded for one file must have - * contiguous numbers, starting with `1`. - * @param string $range The range of bytes to copy. - * If not provided, the whole source file will be copied. - * @param array $sourceSSE Specifies the parameters for B2 to use for accessing the - * source file data using Server-Side Encryption. - * @param array $destinationSSE Specifies the parameters for B2 to use for encrypting the - * copied data before storing the destination file using Server-Side Encryption. - */ - public function copyPart( - string $sourceFileId, - string $largeFileId, - int $partNumber, - ?string $range = null, - ?ServerSideEncryption $sourceSSE = null, - ?ServerSideEncryption $destinationSSE = null - ): File { - $response = $this->http->request('POST', 'b2_copy_part', [ - 'json' => Utils::filterRequestOptions([ - File::ATTRIBUTE_SOURCE_FILE_ID => $sourceFileId, - File::ATTRIBUTE_LARGE_FILE_ID => $largeFileId, - File::ATTRIBUTE_PART_NUMBER => $partNumber, - ], [ - File::ATTRIBUTE_RANGE => $range, - File::ATTRIBUTE_SOURCE_SSE => $sourceSSE, - File::ATTRIBUTE_DESTINATION_SSE => $destinationSSE, - ]), - ]); - - return File::fromArray(json_decode((string) $response->getBody(), true)); + return File::fromArray(Utils::jsonDecode($response)); } /** * Deletes one version of a file. * * @link https://www.backblaze.com/b2/docs/b2_delete_file_version.html + * + * @b2-capability deleteFiles + * @b2-transaction Class A * * @param string $fileName The name of the file. * @param string $fileId The ID of the file. * @param bool $bypassGovernance Must be specified and set to true if deleting a file version protected by * File Lock governance mode retention settings. */ - public function deleteFileVersion(string $fileId, string $fileName, ?bool $bypassGovernance = false): File - { - $response = $this->http->request('POST', 'b2_delete_file_version', [ + public function deleteFileVersion( + string $fileId, + ?string $fileName = null, + ?bool $bypassGovernance = false + ): File { + $response = $this->http->request('POST', Endpoint::DELETE_FILE_VERSION, [ 'json' => Utils::filterRequestOptions([ - File::ATTRIBUTE_FILE_NAME => $fileName, File::ATTRIBUTE_FILE_ID => $fileId, + File::ATTRIBUTE_FILE_NAME => $fileName ?? $this->getFileById($fileId)->getId(), ], [ File::ATTRIBUTE_BYPASS_GOVERNANCE => $bypassGovernance, ]), ]); - return File::fromArray(json_decode((string) $response->getBody(), true)); - } - - /** - * Downloads one file from B2 by File ID. - * - * @link https://www.backblaze.com/b2/docs/b2_download_file_by_id.html - * - * @param string $fileId The file ID to download. - * @param DownloadOptions|array $options An optional array of additional B2 API options. - * @param string $range A standard RFC 7233 byte-range request, that will only return part of the stored file. - * @param string|resource $sink A string, stream, or `StreamInterface` that specifies where to save the file. - * {@link https://docs.guzzlephp.org/en/stable/request-options.html#sink} - * @param bool $headersOnly Only get the file headers, without downloading the whole file. - */ - public function downloadFileById( - string $fileId, - $options = null, - $sink = null, - ?bool $headersOnly = false - ): FileDownload { - return DownloadHelper::instance($this)->download( - Utils::joinPaths( - $this->accountAuthorization()->getDownloadUrl(), - Client::B2_API_VERSION, - 'b2_download_file_by_id' - ), - [File::ATTRIBUTE_FILE_ID => $fileId], - $options, - $sink, - $headersOnly - ); - } - - /** - * Downloads one file from B2 by File Name. - * - * @link https://www.backblaze.com/b2/docs/b2_download_file_by_name.html - * - * @param string $fileName The file name to download. - * @param string $bucketName The bucket the file is contained in. - * @param DownloadOptions|array $options An optional array of additional B2 API options. - * @param string $range A standard RFC 7233 byte-range request, that will only return part of the stored file. - * @param string|resource $sink A string, stream, or `StreamInterface` that specifies where to save the file. - * {@link https://docs.guzzlephp.org/en/stable/request-options.html#sink} - * @param bool $headersOnly Only get the file headers, without downloading the whole file. - */ - public function downloadFileByName( - string $fileName, - ?string $bucketName = null, - $options = null, - $sink = null, - ?bool $headersOnly = false - ): FileDownload { - return DownloadHelper::instance($this)->download( - Utils::joinPaths( - $this->accountAuthorization()->getApiUrl(), - 'file', - $bucketName ?? $this->getAllowedBucketName(), - $fileName - ), - null, - $options, - $sink, - $headersOnly - ); - } - - /** - * Converts the parts that have been uploaded into a single B2 file. - * - * @link https://www.backblaze.com/b2/docs/b2_finish_large_file.html - * - * @param string $fileId The ID of the large file. - * @param string[] $hashes An array of SHA1 checksums of the parts of the large file. - * - * @return File - */ - public function finishLargeFile(string $fileId, array $hashes) - { - $response = $this->http->request('POST', 'b2_finish_large_file', [ - 'json' => [ - File::ATTRIBUTE_FILE_ID => $fileId, - File::ATTRIBUTE_PART_SHA1_ARRAY => $hashes, - ] - ]); - - return File::fromArray(json_decode((string) $response->getBody(), true)); - } - - /** - * Generates an authorization token that can be used to download files - * with the specified prefix from a private B2 bucket. - * - * @link https://www.backblaze.com/b2/docs/b2_get_download_authorization.html - * - * @param string $bucketId The identifier for the bucket. - * @param string $fileNamePrefix The file name prefix of files the download authorization token will allow access. - * @param int $validDuration The number of seconds before the authorization token will expire. The minimum - * value is `1` second. The maximum value is `604800`. Default: `604800`. - * @param DownloadOptions|array $options Additional options to pass to the API. - */ - public function getDownloadAuthorization( - string $fileNamePrefix, - ?string $bucketId = null, - ?int $validDuration = DownloadAuthorization::VALID_DURATION_MAX, - $options = null - ): DownloadAuthorization { - if (!$options instanceof DownloadOptions) { - $options = DownloadOptions::fromArray($options ?? []); - } - - $response = $this->http->request('POST', 'b2_get_download_authorization', [ - 'json' => Utils::filterRequestOptions([ - File::ATTRIBUTE_BUCKET_ID => $bucketId ?? $this->getAllowedBucketId(), - File::ATTRIBUTE_FILE_NAME_PREFIX => $fileNamePrefix, - File::ATTRIBUTE_VALID_DURATION => $validDuration, - ], $options->getAuthorizationOptions()), - ]); - - return DownloadAuthorization::fromArray(json_decode((string) $response->getBody(), true)); + return File::fromArray(Utils::jsonDecode($response)); } /** * Gets information about one file stored in B2. * * @link https://www.backblaze.com/b2/docs/b2_get_file_info.html + * + * @b2-capability readFiles + * @b2-transaction Class B * * @param string $fileId The ID of the file. */ public function getFileInfo(string $fileId): File { - $response = $this->http->request('POST', 'b2_get_file_info', [ + $response = $this->http->request('POST', Endpoint::GET_FILE_INFO, [ 'json' => [ File::ATTRIBUTE_FILE_ID => $fileId ] ]); - return File::fromArray(json_decode((string) $response->getBody(), true)); - } - - /** - * Gets a URL to use for uploading parts of a large file. - * - * @link https://www.backblaze.com/b2/docs/b2_get_upload_part_url.html - * - * @param string $fileId The ID of the large file to upload parts of. - */ - public function getUploadPartUrl(string $fileId): UploadPartUrl - { - $response = $this->http->request('POST', 'b2_get_upload_part_url', [ - 'json' => [ - File::ATTRIBUTE_FILE_ID => $fileId - ] - ]); - - return UploadPartUrl::fromArray(json_decode((string) $response->getBody(), true)); - } - - /** - * Gets a URL and authorization token to use for uploading files. - * - * @link https://www.backblaze.com/b2/docs/b2_get_upload_url.html - * - * @param string $bucketId The ID of the bucket to upload files to. - * - * @return UploadUrl - */ - public function getUploadUrl(?string $bucketId): UploadUrl - { - $response = $this->http->request('POST', 'b2_get_upload_url', [ - 'json' => [ - File::ATTRIBUTE_BUCKET_ID => $bucketId ?? $this->getAllowedBucketId() - ] - ]); - - return UploadUrl::fromArray(json_decode((string) $response->getBody(), true)); + return File::fromArray(Utils::jsonDecode($response)); } /** @@ -377,26 +139,32 @@ public function getUploadUrl(?string $bucketId): UploadUrl * * @link https://www.backblaze.com/b2/docs/b2_hide_file.html * + * @b2-capability writeFiles + * @b2-transaction Class A + * * @param string $bucketId * @param string $fileName */ public function hideFile(string $fileName, ?string $bucketId = null): File { - $response = $this->http->request('POST', 'b2_hide_file', [ + $response = $this->http->request('POST', Endpoint::HIDE_FILE, [ 'json' => [ File::ATTRIBUTE_BUCKET_ID => $bucketId ?? $this->getAllowedBucketId(), File::ATTRIBUTE_FILE_NAME => $fileName, ] ]); - return File::fromArray(json_decode((string) $response->getBody(), true)); + return File::fromArray(Utils::jsonDecode($response)); } /** * Lists the names of all files in a bucket, starting at a given name. * * @link https://www.backblaze.com/b2/docs/b2_list_file_names.html + * + * @b2-capability listFiles + * @b2-transaction Class C * * @param string $bucketId The bucket to look for file names in. * @param string $prefix Files returned will be limited to those with the given prefix. Defaults to the @@ -417,7 +185,7 @@ public function listFileNames( ?string $startFileName = null, ?int $maxFileCount = 1000 ): FileList { - $response = $this->http->request('POST', 'b2_list_file_names', [ + $response = $this->http->request('POST', Endpoint::LIST_FILE_NAMES, [ 'json' => Utils::filterRequestOptions([ File::ATTRIBUTE_BUCKET_ID => $bucketId ?? $this->getAllowedBucketId(), File::ATTRIBUTE_MAX_FILE_COUNT => $maxFileCount, @@ -428,7 +196,7 @@ public function listFileNames( ]), ]); - return FileList::create($response); + return FileList::fromResponse($response); } /** @@ -437,6 +205,9 @@ public function listFileNames( * * @link https://www.backblaze.com/b2/docs/b2_list_file_versions.html * + * @b2-capability listFiles + * @b2-transaction Class C + * * @param string $bucketId The bucket to look for file names in. * @param string $prefix Files returned will be limited to those with the given prefix. Defaults to the * empty string, which matches all files. If not, the first file name after this the @@ -459,7 +230,7 @@ public function listFileVersions( ?string $startFileId = null, ?int $maxFileCount = 1000 ): FileList { - $response = $this->http->request('POST', 'b2_list_file_versions', [ + $response = $this->http->request('POST', Endpoint::LIST_FILE_VERSIONS, [ 'json' => Utils::filterRequestOptions([ File::ATTRIBUTE_BUCKET_ID => $bucketId ?? $this->getAllowedBucketId(), ], [ @@ -471,109 +242,7 @@ public function listFileVersions( ]), ]); - return FileList::create($response); - } - - /** - * Lists the parts that have been uploaded for a large file that has not been finished yet. - * - * @link https://www.backblaze.com/b2/docs/b2_list_parts.html - * - * @param string $fileId The ID returned by b2_start_large_file. This is the file whose parts will be - * listed. - * @param int $startPartNumber The first part to return. Used when a query hits the maxKeyCount, and you want to - * get more. - * @param int $maxPartCount The maximum number of parts to return in the response. The default value is 1000, - * and the maximum is 10000. The maximum number of parts returned per transaction - * is 1000. - * If more than 1000 are returned, the call will be billed as multiple transactions. - * @param bool $loop Make API requests until there are no keys left. - */ - public function listParts( - string $fileId, - ?int $startPartNumber = null, - ?int $maxPartCount = 1000 - ): FilePartList { - $response = $this->http->request('POST', 'b2_list_parts', [ - 'json' => Utils::filterRequestOptions([ - File::ATTRIBUTE_FILE_ID => $fileId - ], [ - File::ATTRIBUTE_START_PART_NUMBER => $startPartNumber, - File::ATTRIBUTE_MAX_PART_COUNT => $maxPartCount, - ]), - ]); - - return FilePartList::create($response); - } - - /** - * Lists information about large file uploads that have been started, - * but that have not been finished or canceled. - * - * @link https://www.backblaze.com/b2/docs/b2_list_unfinished_large_files.html - * - * @param string $bucketId The bucket to look for file names in. - * @param string $namePrefix Only return files whose names match this prefix. - * @param string $startFileId The first upload to return. - * @param int $maxFileCount The maximum number of files to return from this call. The default value is 100, and - * the maximum allowed is 100. - */ - public function listUnfinishedLargeFiles( - ?string $bucketId = null, - ?string $namePrefix = null, - ?string $startFileId = null, - ?int $maxFileCount = 100 - ): FileList { - $response = $this->http->request('POST', 'b2_list_unfinished_large_files', [ - 'json' => Utils::filterRequestOptions([ - File::ATTRIBUTE_BUCKET_ID => $bucketId ?? $this->getAllowedBucketId(), - ], [ - File::ATTRIBUTE_NAME_PREFIX => $namePrefix, - File::ATTRIBUTE_START_FILE_ID => $startFileId, - File::ATTRIBUTE_MAX_FILE_COUNT => $maxFileCount, - ]), - ]); - - return FileList::create($response); - } - - /** - * Prepares for uploading the parts of a large file. - * - * @link https://www.backblaze.com/b2/docs/b2_start_large_file.html - * - * @param string $bucketId The ID of the bucket that the file will go in. - * @param string $fileName The name of the file. - * @param string $contentType The MIME type of the content of the file. - * @param FileInfo|array $fileInfo A JSON object holding the name/value pairs for the custom file info. - */ - public function startLargeFile( - string $fileName, - ?string $bucketId = null, - ?string $contentType = null, - $fileInfo = null, - ?array $fileRetention = null, - ?array $legalHold = null, - ?array $serverSideEncryption = null - ): File { - if (!$fileInfo instanceof FileInfo) { - $fileInfo = FileInfo::fromArray($fileInfo); - } - - $response = $this->http->request('POST', 'b2_start_large_file', [ - 'json' => Utils::filterRequestOptions([ - File::ATTRIBUTE_BUCKET_ID => $bucketId ?? $this->getAllowedBucketId(), - File::ATTRIBUTE_FILE_NAME => $fileName, - File::ATTRIBUTE_CONTENT_TYPE => $contentType ?? File::CONTENT_TYPE_AUTO, - ], [ - File::ATTRIBUTE_FILE_INFO => $fileInfo->get(), - File::ATTRIBUTE_FILE_RETENTION => $fileRetention, - File::ATTRIBUTE_LEGAL_HOLD => $legalHold, - File::ATTRIBUTE_SSE => $serverSideEncryption, - ]) - ]); - - return File::fromArray(json_decode((string) $response->getBody(), true)); + return FileList::fromResponse($response); } /** @@ -581,24 +250,27 @@ public function startLargeFile( * * @link https://www.backblaze.com/b2/docs/b2_update_file_legal_hold.html * + * @b2-capability writeFileLegalHolds + * @b2-transaction Class A + * * @param string $fileName The name of the file. * @param string $fileId The ID of the file. * @param string $legalHold The legal hold status of the file. `on` = *enabled*; `off` = *disabled*. */ public function updateFileLegalHold( - string $fileName, string $fileId, + ?string $fileName = null, string $legalHold ): File { - $response = $this->http->request('POST', 'b2_update_file_legal_hold', [ + $response = $this->http->request('POST', Endpoint::UPDATE_FILE_LEGAL_HOLD, [ 'json' => Utils::filterRequestOptions([ - File::ATTRIBUTE_FILE_NAME => $fileName, + File::ATTRIBUTE_FILE_NAME => $fileName ?? $this->getFileById($fileId)->getId(), File::ATTRIBUTE_FILE_ID => $fileId, File::ATTRIBUTE_LEGAL_HOLD => $legalHold, ]), ]); - return File::fromArray(json_decode((string) $response->getBody(), true)); + return File::fromArray(Utils::jsonDecode($response)); } /** @@ -606,126 +278,31 @@ public function updateFileLegalHold( * * @link https://www.backblaze.com/b2/docs/b2_update_file_retention.html * + * @b2-capability writeFileRetentions + * @b2-transaction Class A + * * @param string $fileName The name of the file. * @param string $fileId The ID of the file. - * @param string $fileRetention The legal hold status of the file. `on` = *enabled*; `off` = *disabled*. + * @param array $fileRetention The file retention settings. * @param bool $bypassGovernance To shorten or remove an existing governance mode retention setting, * this must also be specified and set to `true`. */ public function updateFileRetention( - string $fileName, string $fileId, - string $fileRetention, + ?string $fileName = null, + array $fileRetention, ?bool $bypassGovernance = false ): File { - $response = $this->http->request('POST', 'b2_update_file_retention', [ + $response = $this->http->request('POST', Endpoint::UPDATE_FILE_RETENTION, [ 'json' => Utils::filterRequestOptions([ - File::ATTRIBUTE_FILE_NAME => $fileName, + File::ATTRIBUTE_FILE_NAME => $fileName ?? $this->getFileById($fileId)->getId(), File::ATTRIBUTE_FILE_ID => $fileId, File::ATTRIBUTE_FILE_RETENTION => $fileRetention, File::ATTRIBUTE_BYPASS_GOVERNANCE => $bypassGovernance, ]), ]); - return File::fromArray(json_decode((string) $response->getBody(), true)); - } - - /** - * Uploads a file to a bucket and returns the uploaded file as an object. - * - * @link https://www.backblaze.com/b2/docs/b2_upload_file.html - * - * @param string $bucketId The ID of the bucket to upload the file to. - * @param string $fileName The name of the file. - * @param string|resource $body The file to be uploaded. String or stream resource. - * @param string $contentType The MIME type of the content of the file. - * @param FileInfo|array $fileInfo The custom file info to add to the uploaded file. - * @param ServerSideEncryption|array $serverSideEncryption The parameters for B2 to encrypt the uploaded file. - * @param UploadUrl $uploadUrl The upload authorization data. - */ - public function uploadFile( - string $fileName, - ?string $bucketId = null, - $body, - ?string $contentType = null, - $fileInfo = null, - $serverSideEncryption = null, - ?UploadUrl $uploadUrl = null - ): File { - if (!$fileInfo instanceof FileInfo) { - $fileInfo = FileInfo::fromArray($fileInfo ?? []); - } - - if (!$serverSideEncryption instanceof ServerSideEncryption) { - $serverSideEncryption = ServerSideEncryption::fromArray($serverSideEncryption ?? []); - } - - if (!$uploadUrl instanceof UploadUrl) { - $uploadUrl = $this->getUploadUrl($bucketId); - } - - $uploadMetadata = FileUploadMetadata::fromResource($body); - $mtime = $uploadMetadata->getLastModifiedTimestamp(); - if ($mtime > 0) { - $fileInfo->setLastModifiedTimestamp($mtime); - } - - $response = $this->http->request('POST', $uploadUrl->getUploadUrl(), [ - 'body' => $body, - 'headers' => Utils::filterRequestOptions( - [ - 'Authorization' => $uploadUrl->getAuthorizationToken(), - 'Content-Type' => $contentType ?? File::CONTENT_TYPE_AUTO, - 'Content-Length' => $uploadMetadata->getLength(), - File::HEADER_X_BZ_CONTENT_SHA1 => $uploadMetadata->getSha1(), - File::HEADER_X_BZ_FILE_NAME => $fileName, //rawurlencode($fileName),// urlencode($fileName), - ], - ($serverSideEncryption->getHeaders() ?? []), - ($fileInfo->getHeaders() ?? []) - ), - ]); - - return File::fromArray(json_decode((string) $response->getBody(), true)); - } - - /** - * Uploads one part of a large file. - * - * @link https://www.backblaze.com/b2/docs/b2_upload_part.html - * - * @param string|resource $body The file part to be uploaded. String or stream resource. - * @param string $fileId The ID of the large file whose parts you want to upload. - * @param int $partNumber The parts uploaded for one file must have contiguous numbers, starting with 1. - * @param ServerSideEncryption|array $serverSideEncryption The parameters for B2 to encrypt the uploaded file. - * @param UploadPartUrl $uploadPartUrl The upload part authorization data. - */ - public function uploadPart( - $body, - string $fileId, - ?int $partNumber = 1, - $serverSideEncryption = null, - ?UploadPartUrl $uploadPartUrl = null, - ?FileUploadMetadata $metadata = null - ): File { - if (!$uploadPartUrl instanceof UploadPartUrl) { - $uploadPartUrl = $this->getUploadPartUrl($fileId); - } - - if (!$metadata instanceof FileUploadMetadata) { - $metadata = FileUploadMetadata::fromResource($body); - } - - $response = $this->http->request('POST', $uploadPartUrl->getUploadUrl(), [ - 'body' => $body, - 'headers' => self::filterRequestOptions([ - 'Authorization' => $uploadPartUrl->getAuthorizationToken(), - 'Content-Length' => $metadata->getLength(), - File::HEADER_X_BZ_CONTENT_SHA1 => $metadata->getSha1(), - File::HEADER_X_BZ_PART_NUMBER => $partNumber, - ], $serverSideEncryption->getHeaders() ?? []), - ]); - - return File::fromArray(json_decode((string) $response->getBody(), true)); + return File::fromArray(Utils::jsonDecode($response)); } /** @@ -802,58 +379,6 @@ public function getFileById(string $fileId, ?string $bucketId = null): File return $file; } - /** - * Internal method to call the b2_list_parts API - * - * @see Client::listParts() - * - * @return iterable - */ - public function listAllParts( - string $fileId, - int $startPartNumber = null - ): iterable { - $allParts = new AppendIterator(); - $nextPartNumber = $startPartNumber ?? 0; - - while ($nextPartNumber !== null) { - $parts = $this->listParts($fileId, $startPartNumber); - $nextPartNumber = $parts->getNextPartNumber(); - - $allParts->append(new NoRewindIterator($parts->getParts())); - } - - return $allParts; - } - - /** - * Lists information about *all* large file uploads that have been started, - * but that have not been finished or canceled. - * - * @see Client::listUnfinishedLargeFiles() - * - * @return iterable - */ - public function listAllUnfinishedLargeFiles( - ?string $bucketId = null, - ?string $namePrefix = null, - ?string $startFileId = null, - ?int $maxFileCount = 100 - ): iterable { - - $allFiles = new AppendIterator(); - $nextFileId = $startFileId ?? ''; - - while ($nextFileId !== null) { - $files = $this->listUnfinishedLargeFiles($bucketId, $namePrefix, $startFileId, $maxFileCount); - $nextFileId = $files->getNextFileId(); - - $allFiles->append(new NoRewindIterator($files->getFiles())); - } - - return $allFiles; - } - /** * Deletes all versions of a file(s) in a bucket. * diff --git a/src/Operations/LargeFileOperationsTrait.php b/src/Operations/LargeFileOperationsTrait.php new file mode 100644 index 0000000..7891324 --- /dev/null +++ b/src/Operations/LargeFileOperationsTrait.php @@ -0,0 +1,346 @@ +http->request('POST', Endpoint::CANCEL_LARGE_FILE, [ + 'json' => [ + File::ATTRIBUTE_FILE_ID => $fileId, + ], + ]); + + return File::fromArray(Utils::jsonDecode($response)); + } + + /** + * Copies from an existing B2 file, storing it as a part of a large file which has already been started with + * `b2_start_large_file`. + * + * @link https://www.backblaze.com/b2/docs/b2_copy_part.html + * + * @b2-capability writeFiles + * @b2-capability [readFiles] If the bucket is private. + * @b2-transaction Class C + * + * @param string $sourceFileId The ID of the source file being copied. + * @param string $largeFileId The ID of the large file the part will belong to. + * @param int $partNumber A number from `1` to `10000`. The parts uploaded for one file must have + * contiguous numbers, starting with `1`. + * @param string $range The range of bytes to copy. + * If not provided, the whole source file will be copied. + * @param array $sourceSSE Specifies the parameters for B2 to use for accessing the + * source file data using Server-Side Encryption. + * @param array $destinationSSE Specifies the parameters for B2 to use for encrypting the + * copied data before storing the destination file using Server-Side Encryption. + */ + public function copyPart( + string $sourceFileId, + string $largeFileId, + int $partNumber, + ?string $range = null, + ?ServerSideEncryption $sourceSSE = null, + ?ServerSideEncryption $destinationSSE = null + ): File { + $response = $this->http->request('POST', Endpoint::COPY_PART, [ + 'json' => Utils::filterRequestOptions([ + File::ATTRIBUTE_SOURCE_FILE_ID => $sourceFileId, + File::ATTRIBUTE_LARGE_FILE_ID => $largeFileId, + File::ATTRIBUTE_PART_NUMBER => $partNumber, + ], [ + File::ATTRIBUTE_RANGE => $range, + File::ATTRIBUTE_SOURCE_SSE => $sourceSSE, + File::ATTRIBUTE_DESTINATION_SSE => $destinationSSE, + ]), + ]); + + return File::fromArray(Utils::jsonDecode($response)); + } + + /** + * Converts the parts that have been uploaded into a single B2 file. + * + * @link https://www.backblaze.com/b2/docs/b2_finish_large_file.html + * + * @b2-capability writeFiles + * @b2-transaction Class A + * + * @param string $fileId The ID of the large file. + * @param string[] $hashes An array of SHA1 checksums of the parts of the large file. + * + * @return File + */ + public function finishLargeFile(string $fileId, array $hashes) + { + $response = $this->http->request('POST', Endpoint::FINISH_LARGE_FILE, [ + 'json' => [ + File::ATTRIBUTE_FILE_ID => $fileId, + File::ATTRIBUTE_PART_SHA1_ARRAY => $hashes, + ] + ]); + + return File::fromArray(Utils::jsonDecode($response)); + } + + /** + * Gets a URL to use for uploading parts of a large file. + * + * @link https://www.backblaze.com/b2/docs/b2_get_upload_part_url.html + * + * @b2-capability writeFiles + * @b2-transaction Class A + * + * @param string $fileId The ID of the large file to upload parts of. + */ + public function getUploadPartUrl(string $fileId): UploadPartUrl + { + $response = $this->http->request('POST', Endpoint::GET_UPLOAD_PART_URL, [ + 'json' => [ + File::ATTRIBUTE_FILE_ID => $fileId + ] + ]); + + return UploadPartUrl::fromArray(Utils::jsonDecode($response)); + } + + /** + * Lists the parts that have been uploaded for a large file that has not been finished yet. + * + * @link https://www.backblaze.com/b2/docs/b2_list_parts.html + * + * @b2-capability writeFiles + * @b2-transaction Class C + * + * @param string $fileId The ID returned by b2_start_large_file. This is the file whose parts will be + * listed. + * @param int $startPartNumber The first part to return. Used when a query hits the maxKeyCount, and you want to + * get more. + * @param int $maxPartCount The maximum number of parts to return in the response. The default value is 1000, + * and the maximum is 10000. The maximum number of parts returned per transaction + * is 1000. + * If more than 1000 are returned, the call will be billed as multiple transactions. + * @param bool $loop Make API requests until there are no keys left. + */ + public function listParts( + string $fileId, + ?int $startPartNumber = null, + ?int $maxPartCount = 1000 + ): FilePartList { + $response = $this->http->request('POST', Endpoint::LIST_PARTS, [ + 'json' => Utils::filterRequestOptions([ + File::ATTRIBUTE_FILE_ID => $fileId + ], [ + File::ATTRIBUTE_START_PART_NUMBER => $startPartNumber, + File::ATTRIBUTE_MAX_PART_COUNT => $maxPartCount, + ]), + ]); + + return FilePartList::fromResponse($response); + } + + /** + * Lists information about large file uploads that have been started, + * but that have not been finished or canceled. + * + * @link https://www.backblaze.com/b2/docs/b2_list_unfinished_large_files.html + * + * @b2-capability listFiles + * @b2-transaction Class C + * + * @param string $bucketId The bucket to look for file names in. + * @param string $namePrefix Only return files whose names match this prefix. + * @param string $startFileId The first upload to return. + * @param int $maxFileCount The maximum number of files to return from this call. The default value is 100, and + * the maximum allowed is 100. + */ + public function listUnfinishedLargeFiles( + ?string $bucketId = null, + ?string $namePrefix = null, + ?string $startFileId = null, + ?int $maxFileCount = 100 + ): FileList { + $response = $this->http->request('POST', Endpoint::LIST_UNFINISHED_LARGE_FILES, [ + 'json' => Utils::filterRequestOptions([ + File::ATTRIBUTE_BUCKET_ID => $bucketId ?? $this->getAllowedBucketId(), + ], [ + File::ATTRIBUTE_NAME_PREFIX => $namePrefix, + File::ATTRIBUTE_START_FILE_ID => $startFileId, + File::ATTRIBUTE_MAX_FILE_COUNT => $maxFileCount, + ]), + ]); + + return FileList::fromResponse($response); + } + + /** + * Prepares for uploading the parts of a large file. + * + * @link https://www.backblaze.com/b2/docs/b2_start_large_file.html + * + * @b2-capability writeFiles + * @b2-transaction Class A + * + * @param string $bucketId The ID of the bucket that the file will go in. + * @param string $fileName The name of the file. + * @param string $contentType The MIME type of the content of the file. + * @param FileInfo|array $fileInfo A JSON object holding the name/value pairs for the custom file info. + */ + public function startLargeFile( + string $fileName, + ?string $bucketId = null, + ?string $contentType = null, + $fileInfo = null, + ?array $fileRetention = null, + ?array $legalHold = null, + ?array $serverSideEncryption = null + ): File { + if (!$fileInfo instanceof FileInfo) { + $fileInfo = FileInfo::fromArray($fileInfo); + } + + $response = $this->http->request('POST', Endpoint::START_LARGE_FILE, [ + 'json' => Utils::filterRequestOptions([ + File::ATTRIBUTE_BUCKET_ID => $bucketId ?? $this->getAllowedBucketId(), + File::ATTRIBUTE_FILE_NAME => $fileName, + File::ATTRIBUTE_CONTENT_TYPE => $contentType ?? File::CONTENT_TYPE_AUTO, + ], [ + File::ATTRIBUTE_FILE_INFO => $fileInfo->get(), + File::ATTRIBUTE_FILE_RETENTION => $fileRetention, + File::ATTRIBUTE_LEGAL_HOLD => $legalHold, + File::ATTRIBUTE_SSE => $serverSideEncryption, + ]) + ]); + + return File::fromArray(Utils::jsonDecode($response)); + } + + /** + * Uploads one part of a large file. + * + * @link https://www.backblaze.com/b2/docs/b2_upload_part.html + * + * @b2-capability writeFiles + * @b2-transaction Class A + * + * @param string|resource $body The file part to be uploaded. String or stream resource. + * @param string $fileId The ID of the large file whose parts you want to upload. + * @param int $partNumber The parts uploaded for one file must have contiguous numbers, starting with 1. + * @param ServerSideEncryption|array $serverSideEncryption The parameters for B2 to encrypt the uploaded file. + * @param UploadPartUrl $uploadPartUrl The upload part authorization data. + */ + public function uploadPart( + $body, + string $fileId, + ?int $partNumber = 1, + $serverSideEncryption = null, + ?UploadPartUrl $uploadPartUrl = null, + ?FileUploadMetadata $metadata = null + ): File { + if (!$uploadPartUrl instanceof UploadPartUrl) { + $uploadPartUrl = $this->getUploadPartUrl($fileId); + } + + if (!$metadata instanceof FileUploadMetadata) { + $metadata = FileUploadMetadata::fromResource($body); + } + + $response = $this->http->request('POST', $uploadPartUrl->getUploadUrl(), [ + 'body' => $body, + 'headers' => self::filterRequestOptions([ + 'Authorization' => $uploadPartUrl->getAuthorizationToken(), + 'Content-Length' => $metadata->getLength(), + File::HEADER_X_BZ_CONTENT_SHA1 => $metadata->getSha1(), + File::HEADER_X_BZ_PART_NUMBER => $partNumber, + ], $serverSideEncryption->getHeaders() ?? []), + ]); + + return File::fromArray(Utils::jsonDecode($response)); + } + + /** + * Internal method to call the b2_list_parts API + * + * @see Client::listParts() + * + * @return iterable + */ + public function listAllParts( + string $fileId, + int $startPartNumber = null + ): iterable { + $allParts = new AppendIterator(); + $nextPartNumber = $startPartNumber ?? 0; + + while ($nextPartNumber !== null) { + $parts = $this->listParts($fileId, $startPartNumber); + $nextPartNumber = $parts->getNextPartNumber(); + + $allParts->append(new NoRewindIterator($parts->getParts())); + } + + return $allParts; + } + + /** + * Lists information about *all* large file uploads that have been started, + * but that have not been finished or canceled. + * + * @see Client::listUnfinishedLargeFiles() + * + * @return iterable + */ + public function listAllUnfinishedLargeFiles( + ?string $bucketId = null, + ?string $namePrefix = null, + ?string $startFileId = null, + ?int $maxFileCount = 100 + ): iterable { + + $allFiles = new AppendIterator(); + $nextFileId = $startFileId ?? ''; + + while ($nextFileId !== null) { + $files = $this->listUnfinishedLargeFiles($bucketId, $namePrefix, $startFileId, $maxFileCount); + $nextFileId = $files->getNextFileId(); + + $allFiles->append(new NoRewindIterator($files->getFiles())); + } + + return $allFiles; + } + +} diff --git a/src/Operations/UploadOperationsTrait.php b/src/Operations/UploadOperationsTrait.php new file mode 100644 index 0000000..ba1b96f --- /dev/null +++ b/src/Operations/UploadOperationsTrait.php @@ -0,0 +1,101 @@ +http->request('POST', Endpoint::GET_UPLOAD_URL, [ + 'json' => [ + File::ATTRIBUTE_BUCKET_ID => $bucketId ?? $this->getAllowedBucketId() + ] + ]); + + return UploadUrl::fromArray(Utils::jsonDecode($response)); + } + + /** + * Uploads a file to a bucket and returns the uploaded file as an object. + * + * @link https://www.backblaze.com/b2/docs/b2_upload_file.html + * + * @b2-capability writeFiles + * @b2-transaction Class A + * + * @param string $bucketId The ID of the bucket to upload the file to. + * @param string $fileName The name of the file. + * @param string|resource $body The file to be uploaded. String or stream resource. + * @param string $contentType The MIME type of the content of the file. + * @param FileInfo|array $fileInfo The custom file info to add to the uploaded file. + * @param ServerSideEncryption|array $serverSideEncryption The parameters for B2 to encrypt the uploaded file. + * @param UploadUrl $uploadUrl The upload authorization data. + */ + public function uploadFile( + string $fileName, + ?string $bucketId = null, + $body, + ?string $contentType = null, + $fileInfo = null, + $serverSideEncryption = null, + ?UploadUrl $uploadUrl = null + ): File { + if (!$fileInfo instanceof FileInfo) { + $fileInfo = FileInfo::fromArray($fileInfo ?? []); + } + + if (!$serverSideEncryption instanceof ServerSideEncryption) { + $serverSideEncryption = ServerSideEncryption::fromArray($serverSideEncryption ?? []); + } + + if (!$uploadUrl instanceof UploadUrl) { + $uploadUrl = $this->getUploadUrl($bucketId); + } + + $uploadMetadata = FileUploadMetadata::fromResource($body); + $mtime = $uploadMetadata->getLastModifiedTimestamp(); + if ($mtime > 0) { + $fileInfo->setLastModifiedTimestamp($mtime); + } + + $response = $this->http->request('POST', $uploadUrl->getUploadUrl(), [ + 'body' => $body, + 'headers' => Utils::filterRequestOptions( + [ + 'Authorization' => $uploadUrl->getAuthorizationToken(), + 'Content-Type' => $contentType ?? File::CONTENT_TYPE_AUTO, + 'Content-Length' => $uploadMetadata->getLength(), + File::HEADER_X_BZ_CONTENT_SHA1 => $uploadMetadata->getSha1(), + File::HEADER_X_BZ_FILE_NAME => $fileName, //rawurlencode($fileName),// urlencode($fileName), + ], + ($serverSideEncryption->getHeaders() ?? []), + ($fileInfo->getHeaders() ?? []) + ), + ]); + + return File::fromArray(Utils::jsonDecode($response)); + } +} \ No newline at end of file diff --git a/src/Response/AbstractListResponse.php b/src/Response/AbstractListResponse.php index 118fb75..b80a48e 100644 --- a/src/Response/AbstractListResponse.php +++ b/src/Response/AbstractListResponse.php @@ -6,7 +6,7 @@ use Zaxbux\BackblazeB2\Traits\ObjectIterableTrait; - +/** @package Zaxbux\BackblazeB2\Response */ abstract class AbstractListResponse extends AbstractResponse { use ObjectIterableTrait; } diff --git a/src/Response/AbstractResponse.php b/src/Response/AbstractResponse.php index 66ff502..ad442d4 100644 --- a/src/Response/AbstractResponse.php +++ b/src/Response/AbstractResponse.php @@ -4,9 +4,11 @@ namespace Zaxbux\BackblazeB2\Response; +use JsonException; use Psr\Http\Message\ResponseInterface; +use Zaxbux\BackblazeB2\Utils; - +/** @package Zaxbux\BackblazeB2\Response */ abstract class AbstractResponse { /** @var ResponseInterface */ @@ -18,7 +20,7 @@ abstract class AbstractResponse { * @param ResponseInterface $response B2 API response. * @return AbstractListResponse */ - abstract public static function create(ResponseInterface $response): AbstractResponse; + abstract public static function fromResponse(ResponseInterface $response): AbstractResponse; public function __construct(ResponseInterface $response) { @@ -34,4 +36,18 @@ public function getRawResponse(): ResponseInterface { return $this->rawResponse; } + + /** + * Decode the response body as JSON and return the array. + * + * @return null|array Returns null if the response could not be decoded as JSON. + */ + public function json(): ?array + { + try { + return Utils::jsonDecode((string) $this->rawResponse->getBody()); + } catch (JsonException $ex) {} + + return null; + } } diff --git a/src/Response/BucketList.php b/src/Response/BucketList.php index 6c92358..123e8d2 100644 --- a/src/Response/BucketList.php +++ b/src/Response/BucketList.php @@ -4,14 +4,14 @@ namespace Zaxbux\BackblazeB2\Response; -use Generator; -use GuzzleHttp\Utils; +use Iterator; use Psr\Http\Message\ResponseInterface; use Zaxbux\BackblazeB2\Object\Bucket; +use Zaxbux\BackblazeB2\Utils; use function iterator_to_array; - +/** @package Zaxbux\BackblazeB2\Response */ class BucketList extends AbstractListResponse { /** @var iterable */ @@ -25,7 +25,7 @@ public function __construct(array $buckets) /** * Get the value of buckets. */ - public function getBuckets(): Generator + public function getBuckets(): Iterator { return $this->buckets; } @@ -35,7 +35,7 @@ public function getBuckets(): Generator * * @return iterable */ - public function getBucketsArray(): iterable + public function getBucketsArray(): array { return iterator_to_array($this->getBuckets()); } @@ -45,10 +45,10 @@ public function getBucketsArray(): iterable * * @return BucketList */ - public static function create(ResponseInterface $response): BucketList + public static function fromResponse(ResponseInterface $response): BucketList { - $responseData = Utils::jsonDecode((string) $response->getBody(), true); + $buckets = Utils::jsonDecode((string) $response->getBody())[Bucket::ATTRIBUTE_BUCKETS]; - return new BucketList($responseData[Bucket::ATTRIBUTE_BUCKETS]); + return new BucketList($buckets); } } \ No newline at end of file diff --git a/src/Response/FileDownload.php b/src/Response/FileDownload.php index 4ddad71..d34b6aa 100644 --- a/src/Response/FileDownload.php +++ b/src/Response/FileDownload.php @@ -12,8 +12,7 @@ /** * A response representing a file download. - * - + * @package Zaxbux\BackblazeB2\Response */ class FileDownload extends AbstractResponse { @@ -100,7 +99,7 @@ public function isFile(): bool * * @return FileDownload */ - public static function create( + public static function fromResponse( ResponseInterface $response, ?string $filePath = null ): FileDownload { diff --git a/src/Response/FileList.php b/src/Response/FileList.php index eb387a7..451725c 100644 --- a/src/Response/FileList.php +++ b/src/Response/FileList.php @@ -2,13 +2,12 @@ namespace Zaxbux\BackblazeB2\Response; -use Generator; -use GuzzleHttp\Utils; use Iterator; use Psr\Http\Message\ResponseInterface; use Zaxbux\BackblazeB2\Object\File; +use Zaxbux\BackblazeB2\Utils; - +/** @package Zaxbux\BackblazeB2\Response */ class FileList extends AbstractListResponse { /** @var Iterator */ @@ -78,7 +77,7 @@ public function getNextFileName(): ?string * * @return FileList */ - public static function create(ResponseInterface $response): FileList + public static function fromResponse(ResponseInterface $response): FileList { $data = Utils::jsonDecode((string) $response->getBody(), true); diff --git a/src/Response/FilePartList.php b/src/Response/FilePartList.php index 929355c..21c3670 100644 --- a/src/Response/FilePartList.php +++ b/src/Response/FilePartList.php @@ -4,10 +4,9 @@ use Psr\Http\Message\ResponseInterface; use Zaxbux\BackblazeB2\Object\File; +use Zaxbux\BackblazeB2\Utils; -use function GuzzleHttp\json_decode; - - +/** @package Zaxbux\BackblazeB2\Response */ class FilePartList extends AbstractListResponse { /** @var iterable */ @@ -47,9 +46,9 @@ public function getNextPartNumber(): ?string * * @return FilePartList */ - public static function create(ResponseInterface $response): FilePartList + public static function fromResponse(ResponseInterface $response): FilePartList { - $responseData = json_decode((string) $response->getBody(), true); + $responseData = Utils::jsonDecode((string) $response->getBody(), true); return new FilePartList( $responseData[File::ATTRIBUTE_PARTS], diff --git a/src/Response/KeyList.php b/src/Response/KeyList.php index fc495e9..0de0bde 100644 --- a/src/Response/KeyList.php +++ b/src/Response/KeyList.php @@ -10,6 +10,7 @@ use Zaxbux\BackblazeB2\Object\Key; +/** @package Zaxbux\BackblazeB2\Response */ class KeyList extends AbstractListResponse { /** @var iterable */ @@ -52,7 +53,7 @@ public function getNextApplicationKeyId(): ?string * * @return KeyList */ - public static function create(ResponseInterface $response): KeyList + public static function fromResponse(ResponseInterface $response): KeyList { $responseData = Utils::jsonDecode((string) $response->getBody(), true); diff --git a/src/Traits/ApplyToAllFileVersionsTrait.php b/src/Traits/ApplyToAllFileVersionsTrait.php new file mode 100644 index 0000000..3a7539b --- /dev/null +++ b/src/Traits/ApplyToAllFileVersionsTrait.php @@ -0,0 +1,22 @@ +withFileName($fileName); + } +} \ No newline at end of file diff --git a/src/Traits/ObjectIterableTrait.php b/src/Traits/ObjectIterableTrait.php index 4d75128..e53eddc 100644 --- a/src/Traits/ObjectIterableTrait.php +++ b/src/Traits/ObjectIterableTrait.php @@ -4,13 +4,13 @@ namespace Zaxbux\BackblazeB2\Traits; +use Generator; use RuntimeException; use Zaxbux\BackblazeB2\Interfaces\B2ObjectInterface; +/** @package Zaxbux\BackblazeB2\Traits */ trait ObjectIterableTrait { - //abstract public static function fromArray(array $data): B2ObjectInterface; - /** * * @param string $object @@ -20,7 +20,7 @@ trait ObjectIterableTrait * * @throws RuntimeException */ - public static function createObjectIterable(string $object, array $data): iterable + public static function createObjectIterable(string $object, array $data): Generator { if (!method_exists($object, 'fromArray')) { throw new RuntimeException($object .' does not implement fromArray() method'); diff --git a/src/Utils.php b/src/Utils.php index 247a7b0..331fad8 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -6,8 +6,10 @@ use InvalidArgumentException; use GuzzleHttp\Utils as GuzzleUtils; +use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; +/** @package Zaxbux\BackblazeB2 */ final class Utils { /** @@ -93,4 +95,13 @@ public static function getUserAgent(string $appName): string { return sprintf('%s %s+php/%s %s', $appName, Client::USER_AGENT_PREFIX . Client::VERSION, PHP_VERSION, GuzzleUtils::defaultUserAgent()); } + + public static function jsonDecode($data): array + { + if ($data instanceof ResponseInterface) { + $data = (string) $data->getBody(); + } + + return json_decode($data, true, 512, JSON_THROW_ON_ERROR); + } } \ No newline at end of file diff --git a/tests/BucketObjectTest.php b/tests/BucketObjectTest.php index dc66864..ae71629 100644 --- a/tests/BucketObjectTest.php +++ b/tests/BucketObjectTest.php @@ -31,7 +31,7 @@ public function testBucketList() public function testBucketListFromResponse() { - $bucketList = BucketList::create(new Response(200, [], json_encode([ + $bucketList = BucketList::fromResponse(new Response(200, [], json_encode([ Bucket::ATTRIBUTE_BUCKETS => static::createBuckets(100), ]))); diff --git a/tests/ClientBucketOperationsTest.php b/tests/ClientBucketOperationsTest.php index 6963b28..5bd3e68 100644 --- a/tests/ClientBucketOperationsTest.php +++ b/tests/ClientBucketOperationsTest.php @@ -2,12 +2,14 @@ namespace tests; +use BlastCloud\Guzzler\Expectation; use Zaxbux\BackblazeB2\Client; use Zaxbux\BackblazeB2\Object\Bucket; use Zaxbux\BackblazeB2\Object\Bucket\BucketType; use Zaxbux\BackblazeB2\Exceptions\Request\B2APIException; use Zaxbux\BackblazeB2\Exceptions\Request\BadRequestException; use Zaxbux\BackblazeB2\Exceptions\Request\DuplicateBucketNameException; +use Zaxbux\BackblazeB2\Http\Endpoint; class ClientBucketOperationsTest extends ClientTestBase { @@ -99,8 +101,8 @@ public function testUpdateBucketToPublic() ); $bucket = $this->client->updateBucket( - 'bucketId', - BucketType::PUBLIC + BucketType::PUBLIC, + 'bucketId' ); $this->assertInstanceOf(Bucket::class, $bucket); @@ -140,9 +142,33 @@ public function testDeleteBucket() $this->guzzler->expects($this->once()) ->post(static::getEndpointUri(Endpoint::DELETE_BUCKET)); - $this->assertInstanceOf(Bucket::class, $this->client->deleteBucket( - 'bucketId' - )); + $bucket = $this->client->deleteBucket('bucketId'); + + $this->assertInstanceOf(Bucket::class, $bucket); + } + + public function testDeleteBucketWithFiles() + { + $this->guzzler->expects($this->once()) + ->post(self::getEndpointUri(Endpoint::LIST_FILE_VERSIONS)); + $this->guzzler->expects($this->exactly(3)) + ->post(self::getEndpointUri(Endpoint::DELETE_FILE_VERSION)); + $this->guzzler->expects($this->once()) + ->post(static::getEndpointUri(Endpoint::DELETE_BUCKET)); + + $this->guzzler->queueResponse( + MockResponse::fromFile('list_file_versions.json'), + ); + + $this->guzzler->queueMany(MockResponse::fromFile('delete_file.json'), 3); + + $this->guzzler->queueResponse( + MockResponse::fromFile('delete_bucket.json'), + ); + + $bucket = $this->client->deleteBucket('bucketId', true); + + $this->assertInstanceOf(Bucket::class, $bucket); } public function testBadJsonThrownDeletingNonExistentBucket() @@ -158,7 +184,7 @@ public function testBadJsonThrownDeletingNonExistentBucket() public function testBucketNotEmptyThrownDeletingNonEmptyBucket() { - $this->expectException(B2APIException::class); + $this->expectException(BadRequestException::class); $this->guzzler->queueResponse( MockResponse::fromFile('bucket_not_empty.json', 400), @@ -166,4 +192,28 @@ public function testBucketNotEmptyThrownDeletingNonEmptyBucket() $this->client->deleteBucket('bucketId'); } + + public function testGetBucketById() + { + $this->guzzler->expects($this->once()) + ->post(static::getEndpointUri(Endpoint::LIST_BUCKETS)); + + $this->guzzler->queueResponse(MockResponse::fromFile('get_bucket.json')); + + $bucket = $this->client->getBucketById('bucketId'); + + static::assertInstanceOf(Bucket::class, $bucket); + } + + public function testGetBucketByName() + { + $this->guzzler->expects($this->once()) + ->post(static::getEndpointUri(Endpoint::LIST_BUCKETS)); + + $this->guzzler->queueResponse(MockResponse::fromFile('get_bucket.json')); + + $bucket = $this->client->getBucketByName('bucket_name'); + + static::assertInstanceOf(Bucket::class, $bucket); + } } diff --git a/tests/ClientCreationTest.php b/tests/ClientCreationTest.php new file mode 100644 index 0000000..8183d20 --- /dev/null +++ b/tests/ClientCreationTest.php @@ -0,0 +1,52 @@ +assertInstanceOf(Client::class, new Client([ + '000000000000bb80000000000', + 'abcdefghijklmnopqrstuvwxyz01234', + ])); + } + + public function testNewClientWithOptionsArray() + { + $client = new Client([ + 'applicationName' => 'app_name', + 'applicationKeyId' => '000000000000bb80000000000', + 'applicationKey' => 'abcdefghijklmnopqrstuvwxyz01234', + ]); + + $this->assertInstanceOf(Client::class, $client); + $this->assertEquals('app_name', $client->getConfig()->applicationName()); + } + + public function testNewClientWithConfig() + { + $client = new Client(new Config( + '000000000000bb80000000000', + 'abcdefghijklmnopqrstuvwxyz01234', + [ + 'applicationName' => 'app_name', + ] + )); + + $this->assertInstanceOf(Client::class, $client); + $this->assertEquals('app_name', $client->getConfig()->applicationName()); + } + + public function testCreateClient() + { + $this->assertInstanceOf(Client::class, Client::create([ + 'applicationKeyId' => '000000000000bb80000000000', + 'applicationKey' => 'abcdefghijklmnopqrstuvwxyz01234', + ])); + } +} diff --git a/tests/ClientExceptionsTest.php b/tests/ClientExceptionsTest.php index 390553b..842fe48 100644 --- a/tests/ClientExceptionsTest.php +++ b/tests/ClientExceptionsTest.php @@ -92,8 +92,8 @@ public function exceptionDataProvider(): array ['range_not_satisfiable', 416, RangeNotSatisfiableException::class], - ['service_unavailable', 503, ServiceUnavailableException::class], - ['bad_request', 503, BadRequestException::class], + //['service_unavailable', 503, ServiceUnavailableException::class], + //['bad_request', 503, BadRequestException::class], ]; } } diff --git a/tests/ClientFileOperationsTest.php b/tests/ClientFileOperationsTest.php index fbbfc24..418f34f 100644 --- a/tests/ClientFileOperationsTest.php +++ b/tests/ClientFileOperationsTest.php @@ -5,129 +5,263 @@ use Zaxbux\BackblazeB2\Response\FileList; use Zaxbux\BackblazeB2\Object\File; use Zaxbux\BackblazeB2\Exceptions\Request\BadRequestException; +use Zaxbux\BackblazeB2\Http\Endpoint; +use Zaxbux\BackblazeB2\Object\File\FileLock; class ClientFileOperationsTest extends ClientTestBase { - public function testListFilesHandlesMultiplePages() + public function testCopyFile() { $this->guzzler->queueResponse( - MockResponse::fromFile('list_files_page1.json'), - MockResponse::fromFile('list_files_page2.json'), + MockResponse::fromFile('copy_file.json'), ); - $files = $this->client->listAllFileNames('bucketId'); + $newFile = $this->client->copyFile( + 'fileId', + 'newFileName' + ); - $this->assertIsIterable($files); - $this->assertInstanceOf(File::class, $files->current()); - $this->assertCount(1500, $files); + $this->assertInstanceOf(File::class, $newFile); + $this->assertEquals('newFileName', $newFile->getName()); + $this->assertEquals('newFileId', $newFile->getId()); } - public function testListFilesReturnsEmptyArrayWithNoFiles() + public function testDeleteFileVersion() { $this->guzzler->queueResponse( - MockResponse::fromFile('list_files_empty.json'), + MockResponse::fromFile('get_file.json'), + MockResponse::fromFile('delete_file.json'), ); - $response = $this->client->listFileNames('bucketId'); - $this->assertInstanceOf(FileList::class, $response); - $files = $response->getFilesArray(); - $this->assertCount(0, $files); + $fileId = $this->client->getFileByName('Test file.bin', 'bucketId')->getId(); + + $this->assertInstanceOf(File::class, $this->client->deleteFileVersion('Test file.bin', $fileId)); } + - public function testGetFile() + public function testDeleteFileWithoutName() { $this->guzzler->queueResponse( - MockResponse::fromFile('get_file.json'), + MockResponse::fromFile('list_file_versions.json'), + MockResponse::fromFile('delete_file.json'), ); - $file = $this->client->getFileById('fileId', 'bucketId'); + $this->guzzler->expects($this->once()) + ->post(static::getEndpointUri(Endpoint::LIST_FILE_VERSIONS)) + ->post(static::getEndpointUri(Endpoint::DELETE_FILE_VERSION)); + + $file = $this->client->deleteFileVersion('fileId'); $this->assertInstanceOf(File::class, $file); } - public function testGettingNonExistentFileThrowsException() + public function testDeletingNonExistentFileThrowsException() { $this->expectException(BadRequestException::class); $this->guzzler->queueResponse( - MockResponse::fromFile('get_file_non_existent.json', 400), + MockResponse::fromFile('delete_file_non_existent.json', 400), ); - $this->client->getFileById('fileId', 'bucketId'); + $this->assertNull($this->client->deleteFileVersion('fileId', 'fileName')); + } + + public function testGetFileInfo() + { + $this->guzzler->expects($this->once()) + ->post(static::getEndpointUri(Endpoint::GET_FILE_INFO)) + ->withJson(['fileId' => 'file_id']); + + $this->guzzler->queueResponse(MockResponse::fromFile('get_file_info.json')); + + $file = $this->client->getFileInfo('file_id'); + + $this->assertInstanceOf(File::class, $file); } - public function testDeleteFile() + public function testHideFile() { $this->guzzler->queueResponse( - MockResponse::fromFile('get_file.json'), - MockResponse::fromFile('delete_file.json'), + MockResponse::fromFile('hide_file.json'), ); - $fileId = $this->client->getFileByName('Test file.bin', 'bucketId')->getId(); + $this->guzzler->expects($this->once()) + ->post(static::getEndpointUri(Endpoint::HIDE_FILE)); - $this->assertInstanceOf(File::class, $this->client->deleteFileVersion('Test file.bin', $fileId)); + $file = $this->client->hideFile('testfile.bin', 'bucketId'); + + $this->assertInstanceOf(File::class, $file); } - - public function testDeleteFileRetrievesFileNameWhenNotProvided() + public function testListFileNames() { $this->guzzler->queueResponse( MockResponse::fromFile('list_file_versions.json'), ); - $this->guzzler->queueMany(MockResponse::fromFile('delete_file.json'), 3); - $this->guzzler->expects($this->once())->post(static::getEndpointUri(Endpoint::LIST_FILE_VERSIONS)); - $this->guzzler->expects($this->exactly(3))->post(static::getEndpointUri(Endpoint::DELETE_FILE_VERSION)); + $this->guzzler->expects($this->once()) + ->post(static::getEndpointUri(Endpoint::LIST_FILE_NAMES)); - $response = $this->client->deleteAllFileVersions('fileId', null, null, null, 'bucketId'); + $response = $this->client->listFileNames('bucketId'); $this->assertInstanceOf(FileList::class, $response); - - $files = $response->getFilesArray(); - $this->assertCount(3, $files); - $this->assertContainsOnlyInstancesOf(File::class, $files); + $this->assertCount(3, $response->getFilesArray()); + $this->assertEquals(null, $response->getNextFileId()); + $this->assertEquals(null, $response->getNextFileName()); } - public function testDeletingNonExistentFileThrowsException() + public function testListFileVersions() { - $this->expectException(BadRequestException::class); + $this->guzzler->queueResponse( + MockResponse::fromFile('list_file_versions.json'), + ); + + $this->guzzler->expects($this->once()) + ->post(static::getEndpointUri(Endpoint::LIST_FILE_VERSIONS)); + $response = $this->client->listFileVersions('bucketId'); + + $this->assertInstanceOf(FileList::class, $response); + $this->assertCount(3, $response->getFilesArray()); + $this->assertEquals(null, $response->getNextFileId()); + $this->assertEquals(null, $response->getNextFileName()); + } + + public function testUpdateLegalFileHold() + { $this->guzzler->queueResponse( - MockResponse::fromFile('delete_file_non_existent.json', 400), + MockResponse::fromFile('update_file_legal_hold.json'), ); - $this->assertNull($this->client->deleteFileVersion('fileId', 'fileName')); + $this->guzzler->expects($this->once()) + ->post(static::getEndpointUri(Endpoint::UPDATE_FILE_LEGAL_HOLD)); + + $file = $this->client->updateFileLegalHold('file_id', 'file_name', FileLock::LEGAL_HOLD_ENABLED); + + $this->assertInstanceOf(File::class, $file); } - public function testCopyFile() + public function testUpdateLegalFileHoldWithoutFileName() { $this->guzzler->queueResponse( - MockResponse::fromFile('copy_file.json'), + MockResponse::fromFile('list_file_versions.json'), + MockResponse::fromFile('update_file_legal_hold.json'), ); - $newFile = $this->client->copyFile( - 'fileId', - 'newFileName' + $this->guzzler->expects($this->once()) + ->post(static::getEndpointUri(Endpoint::UPDATE_FILE_LEGAL_HOLD)); + + $file = $this->client->updateFileLegalHold('file_id', null, FileLock::LEGAL_HOLD_ENABLED); + + $this->assertInstanceOf(File::class, $file); + } + + public function testUpdateFileRetention() + { + $this->guzzler->queueResponse( + MockResponse::fromFile('update_file_retention.json'), ); - $this->assertInstanceOf(File::class, $newFile); - $this->assertEquals('newFileName', $newFile->getName()); - $this->assertEquals('newFileId', $newFile->getId()); + $this->guzzler->expects($this->once()) + ->post(static::getEndpointUri(Endpoint::UPDATE_FILE_RETENTION)); + + $file = $this->client->updateFileRetention('file_id', 'file_name', [ + 'mode' => '', + 'remainUntilTimestamp' => 0 + ]); + + $this->assertInstanceOf(File::class, $file); } - public function testHideFile() + public function testUpdateFileRetentionWithoutFileName() { $this->guzzler->queueResponse( - MockResponse::fromFile('hide_file.json'), + MockResponse::fromFile('list_file_versions.json'), + MockResponse::fromFile('update_file_retention.json'), ); $this->guzzler->expects($this->once()) - ->post(static::getEndpointUri(Endpoint::AUTHORIZE_ACCOUNT)) - ->post(static::getEndpointUri(Endpoint::HIDE_FILE)); + ->post(static::getEndpointUri(Endpoint::UPDATE_FILE_RETENTION)); - $file = $this->client->hideFile('testfile.bin', 'bucketId'); + $file = $this->client->updateFileRetention('file_id', null, [ + 'mode' => '', + 'remainUntilTimestamp' => 0 + ]); $this->assertInstanceOf(File::class, $file); } + + public function testListAllFileNames() + { + $this->guzzler->queueResponse( + MockResponse::fromFile('list_files_page1.json'), + MockResponse::fromFile('list_files_page2.json'), + ); + + $files = $this->client->listAllFileNames('bucketId'); + + $this->assertIsIterable($files); + $this->assertInstanceOf(File::class, $files->current()); + $this->assertCount(1500, $files); + } + + public function testListFileNamesWithNoFiles() + { + $this->guzzler->queueResponse( + MockResponse::fromFile('list_files_empty.json'), + ); + + $response = $this->client->listFileNames('bucketId'); + $this->assertInstanceOf(FileList::class, $response); + $files = $response->getFilesArray(); + $this->assertCount(0, $files); + } + + public function testListAllFileVersions() + { + + } + + public function testGetFileById() + { + $this->guzzler->queueResponse( + MockResponse::fromFile('get_file.json'), + ); + + $file = $this->client->getFileById('fileId', 'bucketId'); + + $this->assertInstanceOf(File::class, $file); + } + + public function testGettingNonExistentFileThrowsException() + { + $this->expectException(BadRequestException::class); + + $this->guzzler->queueResponse( + MockResponse::fromFile('get_file_non_existent.json', 400), + ); + + $this->client->getFileById('fileId', 'bucketId'); + } + + public function testDeleteAllFileVersions() + { + $this->guzzler->queueResponse( + MockResponse::fromFile('list_file_versions.json'), + ); + $this->guzzler->queueMany(MockResponse::fromFile('delete_file.json'), 3); + + $this->guzzler->expects($this->once())->post(static::getEndpointUri(Endpoint::LIST_FILE_VERSIONS)); + $this->guzzler->expects($this->exactly(3))->post(static::getEndpointUri(Endpoint::DELETE_FILE_VERSION)); + + $response = $this->client->deleteAllFileVersions('fileId', null, null, null, 'bucketId'); + + $this->assertInstanceOf(FileList::class, $response); + + $files = $response->getFilesArray(); + $this->assertCount(3, $files); + $this->assertContainsOnlyInstancesOf(File::class, $files); + } + } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 336f89a..e40227d 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -2,6 +2,7 @@ namespace tests; +use GuzzleHttp\ClientInterface; use GuzzleHttp\Psr7\Response; use Zaxbux\BackblazeB2\Client; use Zaxbux\BackblazeB2\Config; @@ -10,33 +11,17 @@ class ClientTest extends ClientTestBase { - protected function afterSetUp() { - return; - } - public function testClient() { $this->assertInstanceOf(Client::class, $this->client); - } - - public function testClientConfig() - { $this->assertInstanceOf(Config::class, $this->client->getConfig()); + $this->assertInstanceOf(ClientInterface::class, $this->client->getHttpClient()); + $this->assertInstanceOf(AccountAuthorization::class, $this->client->accountAuthorization()); + $this->assertEquals('bucket_id', $this->client->getAllowedBucketId()); + $this->assertEquals('bucket_name', $this->client->getAllowedBucketName()); } - public function testClientAuthorizeAccount() - { - $this->guzzler->expects($this->exactly(2)) - ->get(Client::BASE_URI . Client::B2_API_VERSION . Endpoint::AUTHORIZE_ACCOUNT) - ->withHeader('Authorization', 'Basic MDAwMDAwMDAwMDAwYmI4MDAwMDAwMDAwMDphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0'); - - $this->guzzler->queueResponse( - MockResponse::json(static::ACCOUNT_AUTHORIZATION), - ); - - $this->assertInstanceOf(AccountAuthorization::class, $this->client->authorizeAccount()); - } - + /* public function testRetryMiddleware() { $this->guzzler->queueMany(new Response(429, ['Retry-After' => 1]), 4); @@ -48,7 +33,9 @@ public function testRetryMiddleware() return $expect->post(static::getEndpointUri(Endpoint::LIST_BUCKETS)); }); } + */ + /* public function testThrowsTooManyRequestsException() { $this->expectException(TooManyRequestsException::class); @@ -56,7 +43,6 @@ public function testThrowsTooManyRequestsException() $this->guzzler->queueMany(new Response(429, ['Retry-After' => 1]), 5); $this->client->getHttpClient()->request('POST', static::getEndpointUri(Endpoint::LIST_BUCKETS)); - - } + */ } diff --git a/tests/ClientTestBase.php b/tests/ClientTestBase.php index 30a4fe4..678a7ad 100644 --- a/tests/ClientTestBase.php +++ b/tests/ClientTestBase.php @@ -9,6 +9,8 @@ use PHPUnit\Framework\TestCase; use Zaxbux\BackblazeB2\Client; use tests\Traits\EndpointHelpersTrait; +use Zaxbux\BackblazeB2\Http\Endpoint; +use Zaxbux\BackblazeB2\Utils; abstract class ClientTestBase extends TestCase { @@ -20,7 +22,10 @@ abstract class ClientTestBase extends TestCase */ protected const ACCOUNT_AUTHORIZATION = [ "accountId" => "000000000000bb8", - "allowed" => [], + "allowed" => [ + 'bucketId' => 'bucket_id', + 'bucketName' => 'bucket_name' + ], "apiUrl" => "https://apiNNN.backblaze.com.test", "authorizationToken" => "0_0000000000008f80000000000_zzzzzzzz_zzzzzz_acct_zzzzzzzzzzzzzzzzzzzzzzzzzzzz", "downloadUrl" => "https://fNNN.backblaze.com.test", @@ -59,11 +64,15 @@ protected function setUp(): void } protected function afterSetUp() { - $this->guzzler->expects($this->once())->get(Client::BASE_URI . Client::B2_API_VERSION . Endpoint::AUTHORIZE_ACCOUNT); + $this->guzzler->expects($this->atLeast(0)) + ->get(Client::BASE_URI . Client::B2_API_VERSION . Endpoint::AUTHORIZE_ACCOUNT) + ->withHeader('Authorization', 'Basic MDAwMDAwMDAwMDAwYmI4MDAwMDAwMDAwMDphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0') + ->withHeader('User-Agent', Utils::getUserAgent('app_name')); } protected function clientInit() { return [ + 'applicationName' => 'app_name', 'applicationKeyId' => '000000000000bb80000000000', 'applicationKey' => 'abcdefghijklmnopqrstuvwxyz01234', 'handler' => $this->guzzler->getHandlerStack() diff --git a/tests/ClientUploadTest.php b/tests/ClientUploadTest.php index beee87d..ab39091 100644 --- a/tests/ClientUploadTest.php +++ b/tests/ClientUploadTest.php @@ -4,6 +4,7 @@ use Zaxbux\BackblazeB2\Utils as ClientUtils; use Zaxbux\BackblazeB2\Helpers\UploadHelper; +use Zaxbux\BackblazeB2\Http\Endpoint; use Zaxbux\BackblazeB2\Object\File; use Zaxbux\BackblazeB2\Object\File\FileInfo; diff --git a/tests/KeyObjectTest.php b/tests/KeyObjectTest.php index 421f603..cffdb2f 100644 --- a/tests/KeyObjectTest.php +++ b/tests/KeyObjectTest.php @@ -31,7 +31,7 @@ public function testKeyList() public function testKeyListFromResponse() { - $keyList = KeyList::create(new Response(200, [], json_encode([ + $keyList = KeyList::fromResponse(new Response(200, [], json_encode([ Key::ATTRIBUTE_KEYS => static::createKeys(1000), Key::ATTRIBUTE_NEXT_APPLICATION_KEY_ID => null, ]))); diff --git a/tests/Utils.php b/tests/Utils.php index c189ecb..e697b08 100644 --- a/tests/Utils.php +++ b/tests/Utils.php @@ -2,9 +2,6 @@ namespace tests; -use Zaxbux\BackblazeB2\Client; -use Zaxbux\BackblazeB2\Utils as ClientUtils; - class Utils { public static function nowInMilliseconds(): int { diff --git a/tests/responses/bucket_not_empty.json b/tests/responses/bucket_not_empty.json index f828386..8e003d6 100644 --- a/tests/responses/bucket_not_empty.json +++ b/tests/responses/bucket_not_empty.json @@ -1,5 +1,5 @@ { - "code": "cannot_delete_non_empty_bucket", + "code": "bad_request", "message": "Cannot delete non-empty bucket", "status": 400 } \ No newline at end of file diff --git a/tests/responses/get_bucket.json b/tests/responses/get_bucket.json new file mode 100644 index 0000000..7012cce --- /dev/null +++ b/tests/responses/get_bucket.json @@ -0,0 +1,10 @@ +{ + "buckets": [ + { + "bucketId": "4a48fe8875c6214145260818", + "accountId": "30f20426f0b1", + "bucketName" : "Kitten Videos", + "bucketType": "allPrivate" + } + ] +} \ No newline at end of file diff --git a/tests/responses/get_file_info.json b/tests/responses/get_file_info.json new file mode 100644 index 0000000..61faab9 --- /dev/null +++ b/tests/responses/get_file_info.json @@ -0,0 +1,14 @@ +{ + "accountId": "accountId", + "bucketId": "bucketId", + "contentLength": 20, + "contentSha1": "bc77b0349d325be71ed2ca26d5e68173210e9e18", + "contentType": "application/octet-stream", + "fileId": "4_z4c2b953461da9c825f260e1b_f1114dbf5bg9707e8_d20160206_m012226_c001_v1111017_t0010", + "fileInfo": { + "src_last_modified_millis": "1454721688784" + }, + "action": "upload", + "uploadTimestamp": "1465983870000", + "fileName": "Test file.bin" +} \ No newline at end of file diff --git a/tests/responses/update_file_legal_hold.json b/tests/responses/update_file_legal_hold.json new file mode 100644 index 0000000..ec1b913 --- /dev/null +++ b/tests/responses/update_file_legal_hold.json @@ -0,0 +1,5 @@ +{ + "fileId": "4_h4a48fe8875c6214145260818_f000000000000472a_d20210515_m032022_c001_v0000123_t0104", + "fileName": "typing_test.txt", + "legalHold": "on" +} \ No newline at end of file diff --git a/tests/responses/update_file_retention.json b/tests/responses/update_file_retention.json new file mode 100644 index 0000000..9e21273 --- /dev/null +++ b/tests/responses/update_file_retention.json @@ -0,0 +1,8 @@ +{ + "fileId": "4_h4a48fe8875c6214145260818_f000000000000472a_d20210515_m032022_c001_v0000123_t0104", + "fileName": "typing_test.txt", + "fileRetention": { + "mode": "governance", + "retainUntilTimestamp": 1628942493000 + } +} \ No newline at end of file