diff --git a/StorageControl/metadata/V2/StorageControl.php b/StorageControl/metadata/V2/StorageControl.php index 92bb95ee769f..c43fec0a3398 100644 Binary files a/StorageControl/metadata/V2/StorageControl.php and b/StorageControl/metadata/V2/StorageControl.php differ diff --git a/StorageControl/samples/V2/StorageControlClient/delete_folder_recursive.php b/StorageControl/samples/V2/StorageControlClient/delete_folder_recursive.php new file mode 100644 index 000000000000..8489e59e4945 --- /dev/null +++ b/StorageControl/samples/V2/StorageControlClient/delete_folder_recursive.php @@ -0,0 +1,83 @@ +setName($formattedName); + + // Call the API and handle any network failures. + try { + /** @var OperationResponse $response */ + $response = $storageControlClient->deleteFolderRecursive($request); + $response->pollUntilComplete(); + + if ($response->operationSucceeded()) { + printf('Operation completed successfully.' . PHP_EOL); + } else { + /** @var Status $error */ + $error = $response->getError(); + printf('Operation failed with error data: %s' . PHP_EOL, $error->serializeToJsonString()); + } + } catch (ApiException $ex) { + printf('Call failed with message: %s' . PHP_EOL, $ex->getMessage()); + } +} + +/** + * Helper to execute the sample. + * + * This sample has been automatically generated and should be regarded as a code + * template only. It will require modifications to work: + * - It may require correct/in-range values for request initialization. + * - It may require specifying regional endpoints when creating the service client, + * please see the apiEndpoint client configuration option for more details. + */ +function callSample(): void +{ + $formattedName = StorageControlClient::folderName('[PROJECT]', '[BUCKET]', '[FOLDER]'); + + delete_folder_recursive_sample($formattedName); +} +// [END storage_v2_generated_StorageControl_DeleteFolderRecursive_sync] diff --git a/StorageControl/src/V2/Client/StorageControlClient.php b/StorageControl/src/V2/Client/StorageControlClient.php index b0619544d345..523b35f2033b 100644 --- a/StorageControl/src/V2/Client/StorageControlClient.php +++ b/StorageControl/src/V2/Client/StorageControlClient.php @@ -44,6 +44,7 @@ use Google\Cloud\Storage\Control\V2\CreateAnywhereCacheRequest; use Google\Cloud\Storage\Control\V2\CreateFolderRequest; use Google\Cloud\Storage\Control\V2\CreateManagedFolderRequest; +use Google\Cloud\Storage\Control\V2\DeleteFolderRecursiveRequest; use Google\Cloud\Storage\Control\V2\DeleteFolderRequest; use Google\Cloud\Storage\Control\V2\DeleteManagedFolderRequest; use Google\Cloud\Storage\Control\V2\DisableAnywhereCacheRequest; @@ -88,6 +89,7 @@ * @method PromiseInterface createFolderAsync(CreateFolderRequest $request, array $optionalArgs = []) * @method PromiseInterface createManagedFolderAsync(CreateManagedFolderRequest $request, array $optionalArgs = []) * @method PromiseInterface deleteFolderAsync(DeleteFolderRequest $request, array $optionalArgs = []) + * @method PromiseInterface deleteFolderRecursiveAsync(DeleteFolderRecursiveRequest $request, array $optionalArgs = []) * @method PromiseInterface deleteManagedFolderAsync(DeleteManagedFolderRequest $request, array $optionalArgs = []) * @method PromiseInterface disableAnywhereCacheAsync(DisableAnywhereCacheRequest $request, array $optionalArgs = []) * @method PromiseInterface getAnywhereCacheAsync(GetAnywhereCacheRequest $request, array $optionalArgs = []) @@ -594,6 +596,35 @@ public function deleteFolder(DeleteFolderRequest $request, array $callOptions = $this->startApiCall('DeleteFolder', $request, $callOptions)->wait(); } + /** + * Deletes a folder recursively. This operation is only applicable to a + * hierarchical namespace enabled bucket. + * + * The async variant is {@see StorageControlClient::deleteFolderRecursiveAsync()} . + * + * @example samples/V2/StorageControlClient/delete_folder_recursive.php + * + * @param DeleteFolderRecursiveRequest $request A request to house fields associated with the call. + * @param array $callOptions { + * Optional. + * + * @type RetrySettings|array $retrySettings + * Retry settings to use for this call. Can be a {@see RetrySettings} object, or an + * associative array of retry settings parameters. See the documentation on + * {@see RetrySettings} for example usage. + * } + * + * @return OperationResponse + * + * @throws ApiException Thrown if the API call fails. + */ + public function deleteFolderRecursive( + DeleteFolderRecursiveRequest $request, + array $callOptions = [] + ): OperationResponse { + return $this->startApiCall('DeleteFolderRecursive', $request, $callOptions)->wait(); + } + /** * Permanently deletes an empty managed folder. * diff --git a/StorageControl/src/V2/DeleteFolderRecursiveMetadata.php b/StorageControl/src/V2/DeleteFolderRecursiveMetadata.php new file mode 100644 index 000000000000..12efca222871 --- /dev/null +++ b/StorageControl/src/V2/DeleteFolderRecursiveMetadata.php @@ -0,0 +1,112 @@ +google.storage.control.v2.DeleteFolderRecursiveMetadata + */ +class DeleteFolderRecursiveMetadata extends \Google\Protobuf\Internal\Message +{ + /** + * Generic metadata for the long running operation. + * + * Generated from protobuf field .google.storage.control.v2.CommonLongRunningOperationMetadata common_metadata = 1; + */ + protected $common_metadata = null; + /** + * The path of the folder recursively deleted. + * + * Generated from protobuf field string folder_id = 2; + */ + protected $folder_id = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type \Google\Cloud\Storage\Control\V2\CommonLongRunningOperationMetadata $common_metadata + * Generic metadata for the long running operation. + * @type string $folder_id + * The path of the folder recursively deleted. + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Google\Storage\Control\V2\StorageControl::initOnce(); + parent::__construct($data); + } + + /** + * Generic metadata for the long running operation. + * + * Generated from protobuf field .google.storage.control.v2.CommonLongRunningOperationMetadata common_metadata = 1; + * @return \Google\Cloud\Storage\Control\V2\CommonLongRunningOperationMetadata|null + */ + public function getCommonMetadata() + { + return $this->common_metadata; + } + + public function hasCommonMetadata() + { + return isset($this->common_metadata); + } + + public function clearCommonMetadata() + { + unset($this->common_metadata); + } + + /** + * Generic metadata for the long running operation. + * + * Generated from protobuf field .google.storage.control.v2.CommonLongRunningOperationMetadata common_metadata = 1; + * @param \Google\Cloud\Storage\Control\V2\CommonLongRunningOperationMetadata $var + * @return $this + */ + public function setCommonMetadata($var) + { + GPBUtil::checkMessage($var, \Google\Cloud\Storage\Control\V2\CommonLongRunningOperationMetadata::class); + $this->common_metadata = $var; + + return $this; + } + + /** + * The path of the folder recursively deleted. + * + * Generated from protobuf field string folder_id = 2; + * @return string + */ + public function getFolderId() + { + return $this->folder_id; + } + + /** + * The path of the folder recursively deleted. + * + * Generated from protobuf field string folder_id = 2; + * @param string $var + * @return $this + */ + public function setFolderId($var) + { + GPBUtil::checkString($var, True); + $this->folder_id = $var; + + return $this; + } + +} + diff --git a/StorageControl/src/V2/DeleteFolderRecursiveRequest.php b/StorageControl/src/V2/DeleteFolderRecursiveRequest.php new file mode 100644 index 000000000000..272d1b137632 --- /dev/null +++ b/StorageControl/src/V2/DeleteFolderRecursiveRequest.php @@ -0,0 +1,225 @@ +google.storage.control.v2.DeleteFolderRecursiveRequest + */ +class DeleteFolderRecursiveRequest extends \Google\Protobuf\Internal\Message +{ + /** + * Required. Name of the folder being deleted, however all of its contents + * will be deleted too. Format: + * `projects/{project}/buckets/{bucket}/folders/{folder}` + * + * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { + */ + protected $name = ''; + /** + * Optional. Makes the operation only succeed conditional on whether the root + * folder's current metageneration matches the given value. + * + * Generated from protobuf field optional int64 if_metageneration_match = 2 [(.google.api.field_behavior) = OPTIONAL]; + */ + protected $if_metageneration_match = null; + /** + * Optional. Makes the operation only succeed conditional on whether the root + * folder's current metageneration does not match the given value. + * + * Generated from protobuf field optional int64 if_metageneration_not_match = 3 [(.google.api.field_behavior) = OPTIONAL]; + */ + protected $if_metageneration_not_match = null; + /** + * Optional. A unique identifier for this request. UUID is the recommended + * format, but other formats are still accepted. + * + * Generated from protobuf field string request_id = 4 [(.google.api.field_behavior) = OPTIONAL, (.google.api.field_info) = { + */ + protected $request_id = ''; + + /** + * @param string $name Required. Name of the folder being deleted, however all of its contents + * will be deleted too. Format: + * `projects/{project}/buckets/{bucket}/folders/{folder}` + * Please see {@see StorageControlClient::folderName()} for help formatting this field. + * + * @return \Google\Cloud\Storage\Control\V2\DeleteFolderRecursiveRequest + * + * @experimental + */ + public static function build(string $name): self + { + return (new self()) + ->setName($name); + } + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $name + * Required. Name of the folder being deleted, however all of its contents + * will be deleted too. Format: + * `projects/{project}/buckets/{bucket}/folders/{folder}` + * @type int|string $if_metageneration_match + * Optional. Makes the operation only succeed conditional on whether the root + * folder's current metageneration matches the given value. + * @type int|string $if_metageneration_not_match + * Optional. Makes the operation only succeed conditional on whether the root + * folder's current metageneration does not match the given value. + * @type string $request_id + * Optional. A unique identifier for this request. UUID is the recommended + * format, but other formats are still accepted. + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Google\Storage\Control\V2\StorageControl::initOnce(); + parent::__construct($data); + } + + /** + * Required. Name of the folder being deleted, however all of its contents + * will be deleted too. Format: + * `projects/{project}/buckets/{bucket}/folders/{folder}` + * + * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Required. Name of the folder being deleted, however all of its contents + * will be deleted too. Format: + * `projects/{project}/buckets/{bucket}/folders/{folder}` + * + * Generated from protobuf field string name = 1 [(.google.api.field_behavior) = REQUIRED, (.google.api.resource_reference) = { + * @param string $var + * @return $this + */ + public function setName($var) + { + GPBUtil::checkString($var, True); + $this->name = $var; + + return $this; + } + + /** + * Optional. Makes the operation only succeed conditional on whether the root + * folder's current metageneration matches the given value. + * + * Generated from protobuf field optional int64 if_metageneration_match = 2 [(.google.api.field_behavior) = OPTIONAL]; + * @return int|string + */ + public function getIfMetagenerationMatch() + { + return isset($this->if_metageneration_match) ? $this->if_metageneration_match : 0; + } + + public function hasIfMetagenerationMatch() + { + return isset($this->if_metageneration_match); + } + + public function clearIfMetagenerationMatch() + { + unset($this->if_metageneration_match); + } + + /** + * Optional. Makes the operation only succeed conditional on whether the root + * folder's current metageneration matches the given value. + * + * Generated from protobuf field optional int64 if_metageneration_match = 2 [(.google.api.field_behavior) = OPTIONAL]; + * @param int|string $var + * @return $this + */ + public function setIfMetagenerationMatch($var) + { + GPBUtil::checkInt64($var); + $this->if_metageneration_match = $var; + + return $this; + } + + /** + * Optional. Makes the operation only succeed conditional on whether the root + * folder's current metageneration does not match the given value. + * + * Generated from protobuf field optional int64 if_metageneration_not_match = 3 [(.google.api.field_behavior) = OPTIONAL]; + * @return int|string + */ + public function getIfMetagenerationNotMatch() + { + return isset($this->if_metageneration_not_match) ? $this->if_metageneration_not_match : 0; + } + + public function hasIfMetagenerationNotMatch() + { + return isset($this->if_metageneration_not_match); + } + + public function clearIfMetagenerationNotMatch() + { + unset($this->if_metageneration_not_match); + } + + /** + * Optional. Makes the operation only succeed conditional on whether the root + * folder's current metageneration does not match the given value. + * + * Generated from protobuf field optional int64 if_metageneration_not_match = 3 [(.google.api.field_behavior) = OPTIONAL]; + * @param int|string $var + * @return $this + */ + public function setIfMetagenerationNotMatch($var) + { + GPBUtil::checkInt64($var); + $this->if_metageneration_not_match = $var; + + return $this; + } + + /** + * Optional. A unique identifier for this request. UUID is the recommended + * format, but other formats are still accepted. + * + * Generated from protobuf field string request_id = 4 [(.google.api.field_behavior) = OPTIONAL, (.google.api.field_info) = { + * @return string + */ + public function getRequestId() + { + return $this->request_id; + } + + /** + * Optional. A unique identifier for this request. UUID is the recommended + * format, but other formats are still accepted. + * + * Generated from protobuf field string request_id = 4 [(.google.api.field_behavior) = OPTIONAL, (.google.api.field_info) = { + * @param string $var + * @return $this + */ + public function setRequestId($var) + { + GPBUtil::checkString($var, True); + $this->request_id = $var; + + return $this; + } + +} + diff --git a/StorageControl/src/V2/gapic_metadata.json b/StorageControl/src/V2/gapic_metadata.json index 3ed2f81e9f0a..86b444f90e16 100644 --- a/StorageControl/src/V2/gapic_metadata.json +++ b/StorageControl/src/V2/gapic_metadata.json @@ -30,6 +30,11 @@ "deleteFolder" ] }, + "DeleteFolderRecursive": { + "methods": [ + "deleteFolderRecursive" + ] + }, "DeleteManagedFolder": { "methods": [ "deleteManagedFolder" diff --git a/StorageControl/src/V2/resources/storage_control_client_config.json b/StorageControl/src/V2/resources/storage_control_client_config.json index 2c9634fe7301..93b69f4f75bf 100644 --- a/StorageControl/src/V2/resources/storage_control_client_config.json +++ b/StorageControl/src/V2/resources/storage_control_client_config.json @@ -62,6 +62,11 @@ "retry_codes_name": "no_retry_1_codes", "retry_params_name": "no_retry_1_params" }, + "DeleteFolderRecursive": { + "timeout_millis": 60000, + "retry_codes_name": "retry_policy_1_codes", + "retry_params_name": "retry_policy_1_params" + }, "DeleteManagedFolder": { "timeout_millis": 60000, "retry_codes_name": "no_retry_1_codes", diff --git a/StorageControl/src/V2/resources/storage_control_descriptor_config.php b/StorageControl/src/V2/resources/storage_control_descriptor_config.php index 27bc04c0f18d..68d8f4d8702d 100644 --- a/StorageControl/src/V2/resources/storage_control_descriptor_config.php +++ b/StorageControl/src/V2/resources/storage_control_descriptor_config.php @@ -45,6 +45,31 @@ 'requestId' => \Google\Api\FieldInfo\Format::UUID4, ], ], + 'DeleteFolderRecursive' => [ + 'longRunning' => [ + 'operationReturnType' => '\Google\Protobuf\GPBEmpty', + 'metadataReturnType' => '\Google\Cloud\Storage\Control\V2\DeleteFolderRecursiveMetadata', + 'initialPollDelayMillis' => '500', + 'pollDelayMultiplier' => '1.5', + 'maxPollDelayMillis' => '5000', + 'totalPollTimeoutMillis' => '300000', + ], + 'callType' => \Google\ApiCore\Call::LONGRUNNING_CALL, + 'headerParams' => [ + [ + 'keyName' => 'bucket', + 'fieldAccessors' => [ + 'getName', + ], + 'matchers' => [ + '/^(?projects\/[^\/]+\/buckets\/[^\/]+)(?:\/.*)?$/', + ], + ], + ], + 'autoPopulatedFields' => [ + 'requestId' => \Google\Api\FieldInfo\Format::UUID4, + ], + ], 'RenameFolder' => [ 'longRunning' => [ 'operationReturnType' => '\Google\Cloud\Storage\Control\V2\Folder', diff --git a/StorageControl/tests/Unit/V2/Client/StorageControlClientTest.php b/StorageControl/tests/Unit/V2/Client/StorageControlClientTest.php index 191895a35b46..6e6b375e8195 100644 --- a/StorageControl/tests/Unit/V2/Client/StorageControlClientTest.php +++ b/StorageControl/tests/Unit/V2/Client/StorageControlClientTest.php @@ -36,6 +36,7 @@ use Google\Cloud\Storage\Control\V2\CreateAnywhereCacheRequest; use Google\Cloud\Storage\Control\V2\CreateFolderRequest; use Google\Cloud\Storage\Control\V2\CreateManagedFolderRequest; +use Google\Cloud\Storage\Control\V2\DeleteFolderRecursiveRequest; use Google\Cloud\Storage\Control\V2\DeleteFolderRequest; use Google\Cloud\Storage\Control\V2\DeleteManagedFolderRequest; use Google\Cloud\Storage\Control\V2\DisableAnywhereCacheRequest; @@ -462,6 +463,128 @@ public function deleteFolderExceptionTest() $this->assertTrue($transport->isExhausted()); } + /** @test */ + public function deleteFolderRecursiveTest() + { + $operationsTransport = $this->createTransport(); + $operationsClient = new OperationsClient([ + 'apiEndpoint' => '', + 'transport' => $operationsTransport, + 'credentials' => $this->createCredentials(), + ]); + $transport = $this->createTransport(); + $gapicClient = $this->createClient([ + 'transport' => $transport, + 'operationsClient' => $operationsClient, + ]); + $this->assertTrue($transport->isExhausted()); + $this->assertTrue($operationsTransport->isExhausted()); + // Mock response + $incompleteOperation = new Operation(); + $incompleteOperation->setName('operations/deleteFolderRecursiveTest'); + $incompleteOperation->setDone(false); + $transport->addResponse($incompleteOperation); + $expectedResponse = new GPBEmpty(); + $anyResponse = new Any(); + $anyResponse->setValue($expectedResponse->serializeToString()); + $completeOperation = new Operation(); + $completeOperation->setName('operations/deleteFolderRecursiveTest'); + $completeOperation->setDone(true); + $completeOperation->setResponse($anyResponse); + $operationsTransport->addResponse($completeOperation); + // Mock request + $formattedName = $gapicClient->folderName('[PROJECT]', '[BUCKET]', '[FOLDER]'); + $request = (new DeleteFolderRecursiveRequest())->setName($formattedName); + $response = $gapicClient->deleteFolderRecursive($request); + $this->assertFalse($response->isDone()); + $this->assertNull($response->getResult()); + $apiRequests = $transport->popReceivedCalls(); + $this->assertSame(1, count($apiRequests)); + $operationsRequestsEmpty = $operationsTransport->popReceivedCalls(); + $this->assertSame(0, count($operationsRequestsEmpty)); + $actualApiFuncCall = $apiRequests[0]->getFuncCall(); + $actualApiRequestObject = $apiRequests[0]->getRequestObject(); + $this->assertSame('/google.storage.control.v2.StorageControl/DeleteFolderRecursive', $actualApiFuncCall); + $actualValue = $actualApiRequestObject->getName(); + $this->assertProtobufEquals($formattedName, $actualValue); + $expectedOperationsRequestObject = new GetOperationRequest(); + $expectedOperationsRequestObject->setName('operations/deleteFolderRecursiveTest'); + $response->pollUntilComplete([ + 'initialPollDelayMillis' => 1, + ]); + $this->assertTrue($response->isDone()); + $this->assertEquals($expectedResponse, $response->getResult()); + $apiRequestsEmpty = $transport->popReceivedCalls(); + $this->assertSame(0, count($apiRequestsEmpty)); + $operationsRequests = $operationsTransport->popReceivedCalls(); + $this->assertSame(1, count($operationsRequests)); + $actualOperationsFuncCall = $operationsRequests[0]->getFuncCall(); + $actualOperationsRequestObject = $operationsRequests[0]->getRequestObject(); + $this->assertSame('/google.longrunning.Operations/GetOperation', $actualOperationsFuncCall); + $this->assertEquals($expectedOperationsRequestObject, $actualOperationsRequestObject); + $this->assertTrue($transport->isExhausted()); + $this->assertTrue($operationsTransport->isExhausted()); + } + + /** @test */ + public function deleteFolderRecursiveExceptionTest() + { + $operationsTransport = $this->createTransport(); + $operationsClient = new OperationsClient([ + 'apiEndpoint' => '', + 'transport' => $operationsTransport, + 'credentials' => $this->createCredentials(), + ]); + $transport = $this->createTransport(); + $gapicClient = $this->createClient([ + 'transport' => $transport, + 'operationsClient' => $operationsClient, + ]); + $this->assertTrue($transport->isExhausted()); + $this->assertTrue($operationsTransport->isExhausted()); + // Mock response + $incompleteOperation = new Operation(); + $incompleteOperation->setName('operations/deleteFolderRecursiveTest'); + $incompleteOperation->setDone(false); + $transport->addResponse($incompleteOperation); + $status = new stdClass(); + $status->code = Code::DATA_LOSS; + $status->details = 'internal error'; + $expectedExceptionMessage = json_encode( + [ + 'message' => 'internal error', + 'code' => Code::DATA_LOSS, + 'status' => 'DATA_LOSS', + 'details' => [], + ], + JSON_PRETTY_PRINT + ); + $operationsTransport->addResponse(null, $status); + // Mock request + $formattedName = $gapicClient->folderName('[PROJECT]', '[BUCKET]', '[FOLDER]'); + $request = (new DeleteFolderRecursiveRequest())->setName($formattedName); + $response = $gapicClient->deleteFolderRecursive($request); + $this->assertFalse($response->isDone()); + $this->assertNull($response->getResult()); + $expectedOperationsRequestObject = new GetOperationRequest(); + $expectedOperationsRequestObject->setName('operations/deleteFolderRecursiveTest'); + try { + $response->pollUntilComplete([ + 'initialPollDelayMillis' => 1, + ]); + // If the pollUntilComplete() method call did not throw, fail the test + $this->fail('Expected an ApiException, but no exception was thrown.'); + } catch (ApiException $ex) { + $this->assertEquals($status->code, $ex->getCode()); + $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); + } + // Call popReceivedCalls to ensure the stubs are exhausted + $transport->popReceivedCalls(); + $operationsTransport->popReceivedCalls(); + $this->assertTrue($transport->isExhausted()); + $this->assertTrue($operationsTransport->isExhausted()); + } + /** @test */ public function deleteManagedFolderTest() {