Added support for CreateIfNotExists and DeleteIfExists (#1282)

* Added support for CreateIfNotExists and DeleteIfExists

* Added DataLake

* Refined test cases.

* Resolve review comments.

* Resolve more review comments.
This commit is contained in:
Kan Tang 2021-01-11 13:10:17 +08:00 committed by GitHub
parent dc8c9182e1
commit 7615a9ee9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 797 additions and 103 deletions

View File

@ -29,6 +29,7 @@ endif()
set(
AZURE_STORAGE_FILES_DATALAKE_HEADER
inc/azure/storage/files/datalake/protocol/datalake_rest_client.hpp
inc/azure/storage/files/datalake/datalake_constants.hpp
inc/azure/storage/files/datalake/datalake_directory_client.hpp
inc/azure/storage/files/datalake/datalake_file_client.hpp
inc/azure/storage/files/datalake/datalake_file_system_client.hpp

View File

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
namespace Azure { namespace Storage { namespace Files { namespace DataLake { namespace Details {
// Error codes:
constexpr static const char* ContainerAlreadyExists = "ContainerAlreadyExists";
constexpr static const char* ContainerNotFound = "ContainerNotFound";
constexpr static const char* DataLakeFilesystemNotFound = "FilesystemNotFound";
constexpr static const char* DataLakePathNotFound = "PathNotFound";
constexpr static const char* DataLakePathAlreadyExists = "PathAlreadyExists";
}}}}} // namespace Azure::Storage::Files::DataLake::Details

View File

@ -110,6 +110,19 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
return PathClient::Create(Models::PathResourceType::Directory, options);
}
/**
* @brief Create a directory. If it already exists, nothing will happen.
* @param options Optional parameters to create the directory the path points to.
* @return Azure::Core::Response<Models::CreateDirectoryResult> containing the information of
* the created directory
* @remark This request is sent to dfs endpoint.
*/
Azure::Core::Response<Models::CreateDirectoryResult> CreateIfNotExists(
const CreateDirectoryOptions& options = CreateDirectoryOptions()) const
{
return PathClient::CreateIfNotExists(Models::PathResourceType::Directory, options);
}
/**
* @brief Renames a directory. By default, the destination is overwritten and
* if the destination already exists and has a lease the lease is broken.
@ -139,6 +152,19 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
bool recursive,
const DeleteDirectoryOptions& options = DeleteDirectoryOptions()) const;
/**
* @brief Deletes the directory if it already exists.
* @param recursive If "true", all paths beneath the directory will be deleted. If "false" and
* the directory is non-empty, an error occurs.
* @param options Optional parameters to delete the directory the path points to.
* @return Azure::Core::Response<Models::DeleteDirectoryResult> containing the information
* returned when deleting the directory.
* @remark This request is sent to dfs endpoint.
*/
Azure::Core::Response<Models::DeleteDirectoryResult> DeleteIfExists(
bool recursive,
const DeleteDirectoryOptions& options = DeleteDirectoryOptions()) const;
/**
* @brief Sets POSIX access control rights on files and directories under given directory
* recursively.

View File

@ -137,6 +137,19 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
return PathClient::Create(Models::PathResourceType::File, options);
}
/**
* @brief Create a file. If it already exists, it will remain unchanged.
* @param options Optional parameters to create the resource the path points to.
* @return Azure::Core::Response<Models::CreateFileResult> containing the information returned
* when creating the file.
* @remark This request is sent to dfs endpoint.
*/
Azure::Core::Response<Models::CreateFileResult> CreateIfNotExists(
const CreateFileOptions& options = CreateFileOptions()) const
{
return PathClient::CreateIfNotExists(Models::PathResourceType::File, options);
}
/**
* @brief Renames a file. By default, the destination is overwritten and
* if the destination already exists and has a lease the lease is broken.
@ -162,6 +175,15 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
Azure::Core::Response<Models::DeleteFileResult> Delete(
const FileDeleteOptions& options = FileDeleteOptions()) const;
/**
* @brief Deletes the file if it already exists.
* @param options Optional parameters to delete the file the path points to.
* @return Azure::Core::Response<Models::DeleteFileResult>
* @remark This request is sent to dfs endpoint.
*/
Azure::Core::Response<Models::DeleteFileResult> DeleteIfExists(
const FileDeleteOptions& options = FileDeleteOptions()) const;
/**
* @brief Read the contents of a file. For read operations, range requests are supported.
* @param options Optional parameters to read the content from the resource the path points to.

View File

@ -115,6 +115,16 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
Azure::Core::Response<Models::CreateFileSystemResult> Create(
const CreateFileSystemOptions& options = CreateFileSystemOptions()) const;
/**
* @brief Creates the file system if it does not exists.
* @param options Optional parameters to create this file system.
* @return Azure::Core::Response<Models::CreateFileSystemResult> containing the information of
* create a file system. Only valid when successfully created the file system.
* @remark This request is sent to blob endpoint.
*/
Azure::Core::Response<Models::CreateFileSystemResult> CreateIfNotExists(
const CreateFileSystemOptions& options = CreateFileSystemOptions()) const;
/**
* @brief Deletes the file system.
* @param options Optional parameters to delete this file system.
@ -125,6 +135,16 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
Azure::Core::Response<Models::DeleteFileSystemResult> Delete(
const DeleteFileSystemOptions& options = DeleteFileSystemOptions()) const;
/**
* @brief Deletes the file system if it exists.
* @param options Optional parameters to delete this file system.
* @return Azure::Core::Response<Models::DeleteFileSystemResult> containing the information
* returned when deleting file systems. Only valid when successfully deleted the file system.
* @remark This request is sent to blob endpoint.
*/
Azure::Core::Response<Models::DeleteFileSystemResult> DeleteIfExists(
const DeleteFileSystemOptions& options = DeleteFileSystemOptions()) const;
/**
* @brief Sets the metadata of file system.
* @param metadata User-defined metadata to be stored with the filesystem. Note that the string

View File

@ -96,6 +96,19 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
Models::PathResourceType type,
const CreatePathOptions& options = CreatePathOptions()) const;
/**
* @brief Creates a file or directory. By default, the destination is not changed if it already
* exists.
* @param options Optional parameters to create the resource the path points to.
* @return Azure::Core::Response<Models::CreatePathResult> containing the information returned
* when creating a path, the information will only be valid when the create operation is
* successful.
* @remark This request is sent to dfs endpoint.
*/
Azure::Core::Response<Models::CreatePathResult> CreateIfNotExists(
Models::PathResourceType type,
const CreatePathOptions& options = CreatePathOptions()) const;
/**
* @brief Deletes the resource the path points to.
* @param options Optional parameters to delete the reource the path points to.
@ -106,6 +119,16 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
Azure::Core::Response<Models::DeletePathResult> Delete(
const DeletePathOptions& options = DeletePathOptions()) const;
/**
* @brief Deletes the resource the path points to if it exists.
* @param options Optional parameters to delete the reource the path points to.
* @return Azure::Core::Response<Models::DeletePathResult> which is current empty but preserved
* for future usage. The result will only valid if the delete operation is successful.
* @remark This request is sent to dfs endpoint.
*/
Azure::Core::Response<Models::DeletePathResult> DeleteIfExists(
const DeletePathOptions& options = DeletePathOptions()) const;
/**
* @brief Sets the owner, group, permissions, or access control list for a file or directory.
* Note that Hierarchical Namespace must be enabled for the account in order to use

View File

@ -21,7 +21,6 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { nam
// FileSystemClient models:
using DeleteFileSystemResult = FileSystemDeleteResult;
using ListPathsResult = FileSystemListPathsResult;
struct GetFileSystemPropertiesResult
@ -31,12 +30,28 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { nam
Storage::Metadata Metadata;
};
using CreateFileSystemResult = FileSystemCreateResult;
struct CreateFileSystemResult
{
bool Created = true;
std::string ETag;
Core::DateTime LastModified;
};
struct DeleteFileSystemResult
{
bool Deleted = true;
};
using SetFileSystemMetadataResult = FileSystemCreateResult;
// PathClient models:
using DeletePathResult = PathDeleteResult;
struct DeletePathResult
{
bool Deleted = true;
Azure::Core::Nullable<std::string> ContinuationToken;
};
using AcquirePathLeaseResult = Blobs::Models::AcquireBlobLeaseResult;
using RenewPathLeaseResult = Blobs::Models::RenewBlobLeaseResult;
using ReleasePathLeaseResult = Blobs::Models::ReleaseBlobLeaseResult;
@ -124,6 +139,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { nam
struct CreatePathResult
{
bool Created = true;
std::string ETag;
Core::DateTime LastModified;
Azure::Core::Nullable<int64_t> ContentLength;
@ -161,6 +177,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { nam
struct DeleteFileResult
{
bool Deleted = true;
};
struct DownloadFileToResult
@ -185,6 +202,6 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { nam
using SetDirectoryAccessControlRecursiveResult = PathSetAccessControlRecursiveResult;
using CreateDirectoryResult = CreatePathResult;
using DeleteDirectoryResult = PathDeleteResult;
using DeleteDirectoryResult = DeletePathResult;
}}}}} // namespace Azure::Storage::Files::DataLake::Models

View File

@ -177,7 +177,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
auto result = Details::DataLakeRestClient::Path::Create(
destinationDfsUri, *m_pipeline, options.Context, protocolLayerOptions);
// At this point, there is not more exception thrown, meaning the rename is successful.
auto ret = Models::RenameDirectoryResult();
Models::RenameDirectoryResult ret;
ret.ContinuationToken = std::move(result->ContinuationToken);
return Azure::Core::Response<Models::RenameDirectoryResult>(
std::move(ret), result.ExtractRawResponse());
@ -187,16 +187,24 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
bool recursive,
const DeleteDirectoryOptions& options) const
{
Details::DataLakeRestClient::Path::DeleteOptions protocolLayerOptions;
protocolLayerOptions.ContinuationToken = options.ContinuationToken;
protocolLayerOptions.LeaseIdOptional = options.AccessConditions.LeaseId;
protocolLayerOptions.IfMatch = options.AccessConditions.IfMatch;
protocolLayerOptions.IfNoneMatch = options.AccessConditions.IfNoneMatch;
protocolLayerOptions.IfModifiedSince = options.AccessConditions.IfModifiedSince;
protocolLayerOptions.IfUnmodifiedSince = options.AccessConditions.IfUnmodifiedSince;
protocolLayerOptions.RecursiveOptional = recursive;
return Details::DataLakeRestClient::Path::Delete(
m_dfsUri, *m_pipeline, options.Context, protocolLayerOptions);
DeletePathOptions deleteOptions;
deleteOptions.AccessConditions = options.AccessConditions;
deleteOptions.Context = options.Context;
deleteOptions.ContinuationToken = options.ContinuationToken;
deleteOptions.Recursive = recursive;
return PathClient::Delete(deleteOptions);
}
Azure::Core::Response<Models::DeleteDirectoryResult> DirectoryClient::DeleteIfExists(
bool recursive,
const DeleteDirectoryOptions& options) const
{
DeletePathOptions deleteOptions;
deleteOptions.AccessConditions = options.AccessConditions;
deleteOptions.Context = options.Context;
deleteOptions.ContinuationToken = options.ContinuationToken;
deleteOptions.Recursive = recursive;
return PathClient::DeleteIfExists(deleteOptions);
}
Azure::Core::Response<Models::SetDirectoryAccessControlRecursiveResult>

View File

@ -15,6 +15,7 @@
#include <azure/storage/common/storage_per_retry_policy.hpp>
#include <azure/storage/common/storage_retry_policy.hpp>
#include "azure/storage/files/datalake/datalake_constants.hpp"
#include "azure/storage/files/datalake/datalake_utilities.hpp"
#include "azure/storage/files/datalake/version.hpp"
@ -276,7 +277,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
auto result = Details::DataLakeRestClient::Path::Create(
destinationDfsUri, *m_pipeline, options.Context, protocolLayerOptions);
// At this point, there is not more exception thrown, meaning the rename is successful.
auto ret = Models::RenameFileResult();
Models::RenameFileResult ret;
return Azure::Core::Response<Models::RenameFileResult>(
std::move(ret), result.ExtractRawResponse());
}
@ -284,15 +285,25 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
Azure::Core::Response<Models::DeleteFileResult> FileClient::Delete(
const FileDeleteOptions& options) const
{
Details::DataLakeRestClient::Path::DeleteOptions protocolLayerOptions;
protocolLayerOptions.LeaseIdOptional = options.AccessConditions.LeaseId;
protocolLayerOptions.IfMatch = options.AccessConditions.IfMatch;
protocolLayerOptions.IfNoneMatch = options.AccessConditions.IfNoneMatch;
protocolLayerOptions.IfModifiedSince = options.AccessConditions.IfModifiedSince;
protocolLayerOptions.IfUnmodifiedSince = options.AccessConditions.IfUnmodifiedSince;
auto result = Details::DataLakeRestClient::Path::Delete(
m_dfsUri, *m_pipeline, options.Context, protocolLayerOptions);
auto ret = Models::DeleteFileResult();
DeletePathOptions deleteOptions;
deleteOptions.AccessConditions = options.AccessConditions;
deleteOptions.Context = options.Context;
auto result = PathClient::Delete(deleteOptions);
Models::DeleteFileResult ret;
ret.Deleted = true;
return Azure::Core::Response<Models::DeleteFileResult>(
std::move(ret), result.ExtractRawResponse());
}
Azure::Core::Response<Models::DeleteFileResult> FileClient::DeleteIfExists(
const FileDeleteOptions& options) const
{
DeletePathOptions deleteOptions;
deleteOptions.AccessConditions = options.AccessConditions;
deleteOptions.Context = options.Context;
auto result = PathClient::DeleteIfExists(deleteOptions);
Models::DeleteFileResult ret;
ret.Deleted = result->Deleted;
return Azure::Core::Response<Models::DeleteFileResult>(
std::move(ret), result.ExtractRawResponse());
}

View File

@ -12,6 +12,7 @@
#include <azure/storage/common/storage_per_retry_policy.hpp>
#include <azure/storage/common/storage_retry_policy.hpp>
#include "azure/storage/files/datalake/datalake_constants.hpp"
#include "azure/storage/files/datalake/datalake_directory_client.hpp"
#include "azure/storage/files/datalake/datalake_file_client.hpp"
#include "azure/storage/files/datalake/datalake_path_client.hpp"
@ -194,11 +195,32 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
Models::CreateFileSystemResult ret;
ret.ETag = std::move(result->ETag);
ret.LastModified = std::move(result->LastModified);
ret.Created = true;
return Azure::Core::Response<Models::CreateFileSystemResult>(
std::move(ret), result.ExtractRawResponse());
}
Azure::Core::Response<Models::FileSystemDeleteResult> FileSystemClient::Delete(
Azure::Core::Response<Models::CreateFileSystemResult> FileSystemClient::CreateIfNotExists(
const CreateFileSystemOptions& options) const
{
try
{
return Create(options);
}
catch (StorageException& e)
{
if (e.ErrorCode == Details::ContainerAlreadyExists)
{
Models::CreateFileSystemResult ret;
ret.Created = false;
return Azure::Core::Response<Models::CreateFileSystemResult>(
std::move(ret), std::move(e.RawResponse));
}
throw;
}
}
Azure::Core::Response<Models::DeleteFileSystemResult> FileSystemClient::Delete(
const DeleteFileSystemOptions& options) const
{
Blobs::DeleteBlobContainerOptions blobOptions;
@ -207,11 +229,31 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
blobOptions.AccessConditions.IfUnmodifiedSince = options.AccessConditions.IfUnmodifiedSince;
blobOptions.AccessConditions.LeaseId = options.AccessConditions.LeaseId;
auto result = m_blobContainerClient.Delete(blobOptions);
Models::FileSystemDeleteResult ret;
return Azure::Core::Response<Models::FileSystemDeleteResult>(
Models::DeleteFileSystemResult ret;
ret.Deleted = true;
return Azure::Core::Response<Models::DeleteFileSystemResult>(
std::move(ret), result.ExtractRawResponse());
}
Azure::Core::Response<Models::DeleteFileSystemResult> FileSystemClient::DeleteIfExists(
const DeleteFileSystemOptions& options) const
{
try
{
return Delete(options);
}
catch (StorageException& e)
{
if (e.ErrorCode == Details::ContainerNotFound)
{
Models::DeleteFileSystemResult ret;
ret.Deleted = false;
return Azure::Core::Response<Models::DeleteFileSystemResult>(ret, std::move(e.RawResponse));
}
throw;
}
}
Azure::Core::Response<Models::GetFileSystemPropertiesResult> FileSystemClient::GetProperties(
const GetFileSystemPropertiesOptions& options) const
{

View File

@ -11,6 +11,7 @@
#include <azure/storage/common/storage_per_retry_policy.hpp>
#include <azure/storage/common/storage_retry_policy.hpp>
#include "azure/storage/files/datalake/datalake_constants.hpp"
#include "azure/storage/files/datalake/datalake_utilities.hpp"
#include "azure/storage/files/datalake/version.hpp"
@ -248,7 +249,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
protocolLayerOptions.Permissions = options.Permissions;
auto result = Details::DataLakeRestClient::Path::Create(
m_dfsUri, *m_pipeline, options.Context, protocolLayerOptions);
auto ret = Models::CreatePathResult();
Models::CreatePathResult ret;
ret.ETag = std::move(result->ETag.GetValue());
ret.LastModified = std::move(result->LastModified.GetValue());
ret.ContentLength = std::move(result->ContentLength);
@ -256,6 +257,29 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
std::move(ret), result.ExtractRawResponse());
}
Azure::Core::Response<Models::CreatePathResult> PathClient::CreateIfNotExists(
Models::PathResourceType type,
const CreatePathOptions& options) const
{
try
{
auto createOptions = options;
createOptions.AccessConditions.IfNoneMatch = ETagWildcard;
return Create(type, createOptions);
}
catch (StorageException& e)
{
if (e.ErrorCode == Details::DataLakePathAlreadyExists)
{
Models::CreatePathResult ret;
ret.Created = false;
return Azure::Core::Response<Models::CreatePathResult>(
std::move(ret), std::move(e.RawResponse));
}
throw;
}
}
Azure::Core::Response<Models::DeletePathResult> PathClient::Delete(
const DeletePathOptions& options) const
{
@ -267,8 +291,34 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
protocolLayerOptions.IfModifiedSince = options.AccessConditions.IfModifiedSince;
protocolLayerOptions.IfUnmodifiedSince = options.AccessConditions.IfUnmodifiedSince;
protocolLayerOptions.RecursiveOptional = options.Recursive;
return Details::DataLakeRestClient::Path::Delete(
auto result = Details::DataLakeRestClient::Path::Delete(
m_dfsUri, *m_pipeline, options.Context, protocolLayerOptions);
Models::DeletePathResult ret;
ret.ContinuationToken = std::move(result->ContinuationToken);
ret.Deleted = true;
return Azure::Core::Response<Models::DeletePathResult>(
std::move(ret), result.ExtractRawResponse());
}
Azure::Core::Response<Models::DeletePathResult> PathClient::DeleteIfExists(
const DeletePathOptions& options) const
{
try
{
return Delete(options);
}
catch (StorageException& e)
{
if (e.ErrorCode == Details::DataLakeFilesystemNotFound
|| e.ErrorCode == Details::DataLakePathNotFound)
{
Models::DeletePathResult ret;
ret.Deleted = false;
return Azure::Core::Response<Models::DeletePathResult>(
std::move(ret), std::move(e.RawResponse));
}
throw;
}
}
Azure::Core::Response<Models::GetPathPropertiesResult> PathClient::GetProperties(
@ -332,7 +382,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
{
acl = Models::Acl::DeserializeAcls(result->Acl.GetValue());
}
auto ret = Models::GetPathAccessControlResult{};
Models::GetPathAccessControlResult ret;
ret.ETag = std::move(result->ETag);
ret.LastModified = std::move(result->LastModified);
if (!acl.HasValue())

View File

@ -17,7 +17,7 @@ namespace Azure { namespace Storage { namespace Test {
void DataLakeDirectoryClientTest::SetUpTestSuite()
{
DataLakeFileSystemClientTest::SetUpTestSuite();
m_directoryName = LowercaseRandomString(10);
m_directoryName = RandomString(10);
m_directoryClient = std::make_shared<Files::DataLake::DirectoryClient>(
m_fileSystemClient->GetDirectoryClient(m_directoryName));
m_fileSystemClient->GetFileClient(m_directoryName).Create();
@ -36,7 +36,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::DirectoryClient> directoryClient;
for (int32_t i = 0; i < 5; ++i)
{
auto client = m_fileSystemClient->GetDirectoryClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetDirectoryClient(RandomString());
EXPECT_NO_THROW(client.Create());
directoryClient.emplace_back(std::move(client));
}
@ -50,7 +50,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::DirectoryClient> directoryClient;
for (int32_t i = 0; i < 2; ++i)
{
auto client = m_fileSystemClient->GetDirectoryClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetDirectoryClient(RandomString());
EXPECT_NO_THROW(client.Create());
directoryClient.emplace_back(std::move(client));
}
@ -71,7 +71,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::DirectoryClient> directoryClient;
for (int32_t i = 0; i < 2; ++i)
{
auto client = m_fileSystemClient->GetDirectoryClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetDirectoryClient(RandomString());
EXPECT_NO_THROW(client.Create());
directoryClient.emplace_back(std::move(client));
}
@ -90,13 +90,12 @@ namespace Azure { namespace Storage { namespace Test {
{
// Recursive delete works.
std::vector<Files::DataLake::DirectoryClient> directoryClient;
auto rootDir = LowercaseRandomString();
auto rootDir = RandomString();
auto rootDirClient = m_fileSystemClient->GetDirectoryClient(rootDir);
EXPECT_NO_THROW(rootDirClient.Create());
for (int32_t i = 0; i < 5; ++i)
{
auto client
= m_fileSystemClient->GetDirectoryClient(rootDir + "/" + LowercaseRandomString());
auto client = m_fileSystemClient->GetDirectoryClient(rootDir + "/" + RandomString());
EXPECT_NO_THROW(client.Create());
directoryClient.emplace_back(std::move(client));
}
@ -105,6 +104,30 @@ namespace Azure { namespace Storage { namespace Test {
}
}
TEST_F(DataLakeDirectoryClientTest, CreateDeleteIfExistsDirectory)
{
{
auto client = m_fileSystemClient->GetDirectoryClient(RandomString());
bool created = false;
bool deleted = false;
EXPECT_NO_THROW(created = client.Create()->Created);
EXPECT_TRUE(created);
EXPECT_NO_THROW(created = client.CreateIfNotExists()->Created);
EXPECT_FALSE(created);
EXPECT_NO_THROW(deleted = client.Delete(false)->Deleted);
EXPECT_TRUE(deleted);
EXPECT_NO_THROW(deleted = client.DeleteIfExists(false)->Deleted);
EXPECT_FALSE(deleted);
}
{
auto client = Files::DataLake::DirectoryClient::CreateFromConnectionString(
AdlsGen2ConnectionString(), LowercaseRandomString(), RandomString());
bool deleted = false;
EXPECT_NO_THROW(deleted = client.DeleteIfExists(false)->Deleted);
EXPECT_FALSE(deleted);
}
}
TEST_F(DataLakeDirectoryClientTest, RenameDirectory)
{
{
@ -112,14 +135,14 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::DirectoryClient> directoryClients;
for (int32_t i = 0; i < 5; ++i)
{
auto client = m_fileSystemClient->GetDirectoryClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetDirectoryClient(RandomString());
EXPECT_NO_THROW(client.Create());
directoryClients.emplace_back(std::move(client));
}
std::vector<std::string> newPaths;
for (auto& client : directoryClients)
{
auto newPath = LowercaseRandomString();
auto newPath = RandomString();
EXPECT_NO_THROW(client.Rename(newPath));
newPaths.push_back(newPath);
}
@ -137,7 +160,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::DirectoryClient> directoryClient;
for (int32_t i = 0; i < 2; ++i)
{
auto client = m_fileSystemClient->GetDirectoryClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetDirectoryClient(RandomString());
EXPECT_NO_THROW(client.Create());
directoryClient.emplace_back(std::move(client));
}
@ -147,10 +170,10 @@ namespace Azure { namespace Storage { namespace Test {
Files::DataLake::RenameDirectoryOptions options1;
options1.SourceAccessConditions.IfModifiedSince = response->LastModified;
EXPECT_TRUE(IsValidTime(response->LastModified));
EXPECT_THROW(client.Rename(LowercaseRandomString(), options1), StorageException);
EXPECT_THROW(client.Rename(RandomString(), options1), StorageException);
Files::DataLake::RenameDirectoryOptions options2;
options2.SourceAccessConditions.IfUnmodifiedSince = response->LastModified;
auto newPath = LowercaseRandomString();
auto newPath = RandomString();
EXPECT_NO_THROW(client.Rename(newPath, options2));
EXPECT_NO_THROW(m_fileSystemClient->GetDirectoryClient(newPath).Delete(false));
}
@ -160,7 +183,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::DirectoryClient> directoryClient;
for (int32_t i = 0; i < 2; ++i)
{
auto client = m_fileSystemClient->GetDirectoryClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetDirectoryClient(RandomString());
EXPECT_NO_THROW(client.Create());
directoryClient.emplace_back(std::move(client));
}
@ -169,10 +192,10 @@ namespace Azure { namespace Storage { namespace Test {
auto response = client.GetProperties();
Files::DataLake::RenameDirectoryOptions options1;
options1.SourceAccessConditions.IfNoneMatch = response->ETag;
EXPECT_THROW(client.Rename(LowercaseRandomString(), options1), StorageException);
EXPECT_THROW(client.Rename(RandomString(), options1), StorageException);
Files::DataLake::RenameDirectoryOptions options2;
options2.SourceAccessConditions.IfMatch = response->ETag;
auto newPath = LowercaseRandomString();
auto newPath = RandomString();
EXPECT_NO_THROW(client.Rename(newPath, options2));
EXPECT_NO_THROW(m_fileSystemClient->GetDirectoryClient(newPath).Delete(false));
}
@ -182,7 +205,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::DirectoryClient> directoryClient;
for (int32_t i = 0; i < 2; ++i)
{
auto client = m_fileSystemClient->GetDirectoryClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetDirectoryClient(RandomString());
EXPECT_NO_THROW(client.Create());
directoryClient.emplace_back(std::move(client));
}
@ -192,7 +215,7 @@ namespace Azure { namespace Storage { namespace Test {
options.DestinationFileSystem = LowercaseRandomString();
for (auto& client : directoryClient)
{
EXPECT_THROW(client.Rename(LowercaseRandomString(), options), StorageException);
EXPECT_THROW(client.Rename(RandomString(), options), StorageException);
EXPECT_NO_THROW(client.GetProperties());
}
}
@ -207,7 +230,7 @@ namespace Azure { namespace Storage { namespace Test {
options.DestinationFileSystem = newfileSystemName;
for (auto& client : directoryClient)
{
auto newPath = LowercaseRandomString();
auto newPath = RandomString();
EXPECT_NO_THROW(client.Rename(newPath, options));
EXPECT_NO_THROW(newfileSystemClient->GetDirectoryClient(newPath).Delete(false));
}
@ -231,8 +254,8 @@ namespace Azure { namespace Storage { namespace Test {
{
// Create path with metadata works
auto client1 = m_fileSystemClient->GetDirectoryClient(LowercaseRandomString());
auto client2 = m_fileSystemClient->GetDirectoryClient(LowercaseRandomString());
auto client1 = m_fileSystemClient->GetDirectoryClient(RandomString());
auto client2 = m_fileSystemClient->GetDirectoryClient(RandomString());
Files::DataLake::CreatePathOptions options1;
Files::DataLake::CreatePathOptions options2;
options1.Metadata = metadata1;
@ -284,7 +307,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::DirectoryClient> directoryClient;
for (int32_t i = 0; i < 2; ++i)
{
auto client = m_fileSystemClient->GetDirectoryClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetDirectoryClient(RandomString());
Files::DataLake::CreatePathOptions options;
options.HttpHeaders = httpHeader;
EXPECT_NO_THROW(client.Create(options));
@ -305,9 +328,9 @@ namespace Azure { namespace Storage { namespace Test {
TEST_F(DataLakeDirectoryClientTest, DirectorySetAccessControlRecursive)
{
// Setup directories.
auto rootDirectoryName = LowercaseRandomString();
auto directoryName1 = LowercaseRandomString();
auto directoryName2 = LowercaseRandomString();
auto rootDirectoryName = RandomString();
auto directoryName1 = RandomString();
auto directoryName2 = RandomString();
auto rootDirectoryClient = m_fileSystemClient->GetDirectoryClient(rootDirectoryName);
rootDirectoryClient.Create();
auto directoryClient1
@ -346,7 +369,7 @@ namespace Azure { namespace Storage { namespace Test {
{
{
// Create from connection string validates static creator function and shared key constructor.
auto directoryName = LowercaseRandomString(10);
auto directoryName = RandomString(10);
auto connectionStringClient
= Azure::Storage::Files::DataLake::DirectoryClient::CreateFromConnectionString(
AdlsGen2ConnectionString(), m_fileSystemName, directoryName);
@ -361,7 +384,7 @@ namespace Azure { namespace Storage { namespace Test {
auto clientSecretClient = Azure::Storage::Files::DataLake::DirectoryClient(
Azure::Storage::Files::DataLake::DirectoryClient::CreateFromConnectionString(
AdlsGen2ConnectionString(), m_fileSystemName, LowercaseRandomString(10))
AdlsGen2ConnectionString(), m_fileSystemName, RandomString(10))
.GetUri(),
credential);
@ -371,7 +394,7 @@ namespace Azure { namespace Storage { namespace Test {
{
// Create from Anonymous credential.
auto objectName = LowercaseRandomString(10);
auto objectName = RandomString(10);
auto containerClient = Azure::Storage::Blobs::BlobContainerClient::CreateFromConnectionString(
AdlsGen2ConnectionString(), m_fileSystemName);
Azure::Storage::Blobs::SetBlobContainerAccessPolicyOptions options;

View File

@ -33,7 +33,7 @@ namespace Azure { namespace Storage { namespace Test {
void DataLakeFileClientTest::SetUpTestSuite()
{
DataLakeFileSystemClientTest::SetUpTestSuite();
m_fileName = LowercaseRandomString(10);
m_fileName = RandomString(10);
m_fileClient = std::make_shared<Files::DataLake::FileClient>(
m_fileSystemClient->GetFileClient(m_fileName));
m_fileClient->Create();
@ -52,7 +52,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::FileClient> fileClient;
for (int32_t i = 0; i < 5; ++i)
{
auto client = m_fileSystemClient->GetFileClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetFileClient(RandomString());
EXPECT_NO_THROW(client.Create());
fileClient.emplace_back(std::move(client));
}
@ -66,7 +66,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::FileClient> fileClient;
for (int32_t i = 0; i < 2; ++i)
{
auto client = m_fileSystemClient->GetFileClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetFileClient(RandomString());
EXPECT_NO_THROW(client.Create());
fileClient.emplace_back(std::move(client));
}
@ -87,7 +87,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::FileClient> fileClient;
for (int32_t i = 0; i < 2; ++i)
{
auto client = m_fileSystemClient->GetFileClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetFileClient(RandomString());
EXPECT_NO_THROW(client.Create());
fileClient.emplace_back(std::move(client));
}
@ -104,6 +104,30 @@ namespace Azure { namespace Storage { namespace Test {
}
}
TEST_F(DataLakeFileClientTest, CreateDeleteIfExistsFiles)
{
{
auto client = m_fileSystemClient->GetFileClient(RandomString());
bool created = false;
bool deleted = false;
EXPECT_NO_THROW(created = client.Create()->Created);
EXPECT_TRUE(created);
EXPECT_NO_THROW(created = client.CreateIfNotExists()->Created);
EXPECT_FALSE(created);
EXPECT_NO_THROW(deleted = client.Delete()->Deleted);
EXPECT_TRUE(deleted);
EXPECT_NO_THROW(deleted = client.DeleteIfExists()->Deleted);
EXPECT_FALSE(deleted);
}
{
auto client = Files::DataLake::FileClient::CreateFromConnectionString(
AdlsGen2ConnectionString(), LowercaseRandomString(), RandomString());
bool deleted = false;
EXPECT_NO_THROW(deleted = client.DeleteIfExists()->Deleted);
EXPECT_FALSE(deleted);
}
}
TEST_F(DataLakeFileClientTest, RenameFiles)
{
{
@ -111,14 +135,14 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::FileClient> fileClients;
for (int32_t i = 0; i < 5; ++i)
{
auto client = m_fileSystemClient->GetFileClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetFileClient(RandomString());
EXPECT_NO_THROW(client.Create());
fileClients.emplace_back(std::move(client));
}
std::vector<std::string> newPaths;
for (auto& client : fileClients)
{
auto newPath = LowercaseRandomString();
auto newPath = RandomString();
EXPECT_NO_THROW(client.Rename(newPath));
newPaths.push_back(newPath);
}
@ -136,7 +160,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::FileClient> fileClient;
for (int32_t i = 0; i < 2; ++i)
{
auto client = m_fileSystemClient->GetFileClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetFileClient(RandomString());
EXPECT_NO_THROW(client.Create());
fileClient.emplace_back(std::move(client));
}
@ -145,10 +169,10 @@ namespace Azure { namespace Storage { namespace Test {
auto response = client.GetProperties();
Files::DataLake::RenameFileOptions options1;
options1.SourceAccessConditions.IfModifiedSince = response->LastModified;
EXPECT_THROW(client.Rename(LowercaseRandomString(), options1), StorageException);
EXPECT_THROW(client.Rename(RandomString(), options1), StorageException);
Files::DataLake::RenameFileOptions options2;
options2.SourceAccessConditions.IfUnmodifiedSince = response->LastModified;
auto newPath = LowercaseRandomString();
auto newPath = RandomString();
EXPECT_NO_THROW(client.Rename(newPath, options2));
EXPECT_NO_THROW(m_fileSystemClient->GetDirectoryClient(newPath).Delete(false));
}
@ -158,7 +182,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::FileClient> fileClient;
for (int32_t i = 0; i < 2; ++i)
{
auto client = m_fileSystemClient->GetFileClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetFileClient(RandomString());
EXPECT_NO_THROW(client.Create());
fileClient.emplace_back(std::move(client));
}
@ -167,10 +191,10 @@ namespace Azure { namespace Storage { namespace Test {
auto response = client.GetProperties();
Files::DataLake::RenameFileOptions options1;
options1.SourceAccessConditions.IfNoneMatch = response->ETag;
EXPECT_THROW(client.Rename(LowercaseRandomString(), options1), StorageException);
EXPECT_THROW(client.Rename(RandomString(), options1), StorageException);
Files::DataLake::RenameFileOptions options2;
options2.SourceAccessConditions.IfMatch = response->ETag;
auto newPath = LowercaseRandomString();
auto newPath = RandomString();
EXPECT_NO_THROW(client.Rename(newPath, options2));
EXPECT_NO_THROW(m_fileSystemClient->GetDirectoryClient(newPath).Delete(false));
}
@ -180,7 +204,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::FileClient> fileClient;
for (int32_t i = 0; i < 2; ++i)
{
auto client = m_fileSystemClient->GetFileClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetFileClient(RandomString());
EXPECT_NO_THROW(client.Create());
fileClient.emplace_back(std::move(client));
}
@ -190,7 +214,7 @@ namespace Azure { namespace Storage { namespace Test {
options.DestinationFileSystem = LowercaseRandomString();
for (auto& client : fileClient)
{
EXPECT_THROW(client.Rename(LowercaseRandomString(), options), StorageException);
EXPECT_THROW(client.Rename(RandomString(), options), StorageException);
EXPECT_NO_THROW(client.GetProperties());
}
}
@ -205,7 +229,7 @@ namespace Azure { namespace Storage { namespace Test {
options.DestinationFileSystem = newfileSystemName;
for (auto& client : fileClient)
{
auto newPath = LowercaseRandomString();
auto newPath = RandomString();
EXPECT_NO_THROW(client.Rename(newPath, options));
EXPECT_NO_THROW(newfileSystemClient->GetDirectoryClient(newPath).Delete(false));
}
@ -229,8 +253,8 @@ namespace Azure { namespace Storage { namespace Test {
{
// Create path with metadata works
auto client1 = m_fileSystemClient->GetFileClient(LowercaseRandomString());
auto client2 = m_fileSystemClient->GetFileClient(LowercaseRandomString());
auto client1 = m_fileSystemClient->GetFileClient(RandomString());
auto client2 = m_fileSystemClient->GetFileClient(RandomString());
Files::DataLake::CreateFileOptions options1;
Files::DataLake::CreateFileOptions options2;
options1.Metadata = metadata1;
@ -280,7 +304,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<Files::DataLake::FileClient> fileClient;
for (int32_t i = 0; i < 2; ++i)
{
auto client = m_fileSystemClient->GetFileClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetFileClient(RandomString());
Files::DataLake::CreateFileOptions options;
options.HttpHeaders = httpHeader;
EXPECT_NO_THROW(client.Create(options));
@ -331,7 +355,7 @@ namespace Azure { namespace Storage { namespace Test {
auto buffer = RandomBuffer(bufferSize);
auto bufferStream = std::make_unique<Azure::Core::Http::MemoryBodyStream>(
Azure::Core::Http::MemoryBodyStream(buffer));
auto newFileName = LowercaseRandomString(10);
auto newFileName = RandomString(10);
auto newFileClient = std::make_shared<Files::DataLake::FileClient>(
m_fileSystemClient->GetFileClient(newFileName));
newFileClient->Create();
@ -407,13 +431,13 @@ namespace Azure { namespace Storage { namespace Test {
TEST_F(DataLakeFileClientTest, ScheduleForDeletion)
{
{
auto client = m_fileSystemClient->GetFileClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetFileClient(RandomString());
EXPECT_NO_THROW(client.Create());
EXPECT_NO_THROW(
client.ScheduleDeletion(Files::DataLake::ScheduleFileExpiryOriginType::NeverExpire));
}
{
auto client = m_fileSystemClient->GetFileClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetFileClient(RandomString());
EXPECT_NO_THROW(client.Create());
Files::DataLake::ScheduleFileDeletionOptions options;
EXPECT_THROW(
@ -425,7 +449,7 @@ namespace Azure { namespace Storage { namespace Test {
Files::DataLake::ScheduleFileExpiryOriginType::RelativeToNow, options));
}
{
auto client = m_fileSystemClient->GetFileClient(LowercaseRandomString());
auto client = m_fileSystemClient->GetFileClient(RandomString());
EXPECT_NO_THROW(client.Create());
Files::DataLake::ScheduleFileDeletionOptions options;
EXPECT_THROW(
@ -539,7 +563,7 @@ namespace Azure { namespace Storage { namespace Test {
{
{
// Create from connection string validates static creator function and shared key constructor.
auto fileName = LowercaseRandomString(10);
auto fileName = RandomString(10);
auto connectionStringClient
= Azure::Storage::Files::DataLake::FileClient::CreateFromConnectionString(
AdlsGen2ConnectionString(), m_fileSystemName, fileName);
@ -554,7 +578,7 @@ namespace Azure { namespace Storage { namespace Test {
auto clientSecretClient = Azure::Storage::Files::DataLake::FileClient(
Azure::Storage::Files::DataLake::FileClient::CreateFromConnectionString(
AdlsGen2ConnectionString(), m_fileSystemName, LowercaseRandomString(10))
AdlsGen2ConnectionString(), m_fileSystemName, RandomString(10))
.GetUri(),
credential);
@ -567,7 +591,7 @@ namespace Azure { namespace Storage { namespace Test {
std::vector<uint8_t> blobContent;
blobContent.resize(static_cast<std::size_t>(1_MB));
RandomBuffer(reinterpret_cast<char*>(&blobContent[0]), blobContent.size());
auto objectName = LowercaseRandomString(10);
auto objectName = RandomString(10);
auto containerClient = Azure::Storage::Blobs::BlobContainerClient::CreateFromConnectionString(
AdlsGen2ConnectionString(), m_fileSystemName);
Azure::Storage::Blobs::SetBlobContainerAccessPolicyOptions options;

View File

@ -130,6 +130,42 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_NO_THROW(client.Delete(options2));
}
}
{
// CreateIfNotExists & DeleteIfExists.
{
auto client = Files::DataLake::FileSystemClient::CreateFromConnectionString(
AdlsGen2ConnectionString(), LowercaseRandomString());
EXPECT_NO_THROW(client.Create());
EXPECT_NO_THROW(client.CreateIfNotExists());
EXPECT_NO_THROW(client.Delete());
EXPECT_NO_THROW(client.DeleteIfExists());
}
{
auto client = Files::DataLake::FileSystemClient::CreateFromConnectionString(
AdlsGen2ConnectionString(), LowercaseRandomString());
EXPECT_NO_THROW(client.CreateIfNotExists());
EXPECT_THROW(client.Create(), StorageException);
EXPECT_NO_THROW(client.DeleteIfExists());
}
{
auto client = Files::DataLake::FileSystemClient::CreateFromConnectionString(
AdlsGen2ConnectionString(), LowercaseRandomString());
auto created = client.Create()->Created;
EXPECT_TRUE(created);
auto createResult = client.CreateIfNotExists();
EXPECT_FALSE(createResult->Created);
EXPECT_TRUE(createResult->ETag.empty());
EXPECT_EQ(Core::DateTime(), createResult->LastModified);
auto deleted = client.Delete()->Deleted;
EXPECT_TRUE(deleted);
}
{
auto client = Files::DataLake::FileSystemClient::CreateFromConnectionString(
AdlsGen2ConnectionString(), LowercaseRandomString());
auto deleteResult = client.DeleteIfExists();
EXPECT_FALSE(deleteResult->Deleted);
}
}
}
TEST_F(DataLakeFileSystemClientTest, FileSystemMetadata)

View File

@ -21,6 +21,11 @@
- Removed `Offset` and `Length` pair in options. They are now represented with `Azure::Core::Http::Range`.
- Replace scoped enums that don't support bitwise operations with extensible enum.
- `IsServerEncrypted` member in `DownloadFileToResult`, `UploadFileFromResult`, `FileDownloadResult` and `FileGetPropertiesResult` are no longer nullable.
- Create APIs for Directory and File now returns `FileShareSmbProperties` that aggregates SMB related properties.
### New Features
- Added support for `CreateIfNotExists` for Share and Directory clients, and `DeleteIfExists` for Share, Directory and File clients.
## 12.0.0-beta.5 (2020-11-13)

View File

@ -105,6 +105,15 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
Azure::Core::Response<Models::CreateShareResult> Create(
const CreateShareOptions& options = CreateShareOptions()) const;
/**
* @brief Creates the file share if it does not exist, nothing will happen if the file share already exists.
* @param options Optional parameters to create this file share.
* @return Azure::Core::Response<Models::CreateShareResult> containing the information including
* the version and modified time of a share if it is successfully created.
*/
Azure::Core::Response<Models::CreateShareResult> CreateIfNotExists(
const CreateShareOptions& options = CreateShareOptions()) const;
/**
* @brief Deletes the file share.
* @param options Optional parameters to delete this file share.
@ -114,6 +123,15 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
Azure::Core::Response<Models::DeleteShareResult> Delete(
const DeleteShareOptions& options = DeleteShareOptions()) const;
/**
* @brief Deletes the file share if it exists.
* @param options Optional parameters to delete this file share.
* @return Azure::Core::Response<Models::ShareDeleteResult> currently empty and reserved for
* future usage.
*/
Azure::Core::Response<Models::DeleteShareResult> DeleteIfExists(
const DeleteShareOptions& options = DeleteShareOptions()) const;
/**
* @brief Creates a snapshot for the share.
* @param options Optional parameters to create the share snapshot.

View File

@ -16,6 +16,13 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
constexpr int64_t c_FileUploadDefaultChunkSize = 4 * 1024 * 1024;
constexpr int64_t c_FileDownloadDefaultChunkSize = 4 * 1024 * 1024;
constexpr static const char* c_ShareSnapshotQueryParameter = "sharesnapshot";
// Error codes:
constexpr static const char* ParentNotFound = "ParentNotFound";
constexpr static const char* ResourceNotFound = "ResourceNotFound";
constexpr static const char* ShareAlreadyExists = "ShareAlreadyExists";
constexpr static const char* ShareNotFound = "ShareNotFound";
constexpr static const char* ResourceAlreadyExists = "ResourceAlreadyExists";
} // namespace Details
}}}} // namespace Azure::Storage::Files::Shares

View File

@ -101,6 +101,15 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
Azure::Core::Response<Models::CreateDirectoryResult> Create(
const CreateDirectoryOptions& options = CreateDirectoryOptions()) const;
/**
* @brief Creates the directory if it does not exist.
* @param options Optional parameters to create this directory.
* @return Azure::Core::Response<Models::CreateDirectoryResult> containing the information
* returned when creating the directory if successfully created.
*/
Azure::Core::Response<Models::CreateDirectoryResult> CreateIfNotExists(
const CreateDirectoryOptions& options = CreateDirectoryOptions()) const;
/**
* @brief Deletes the directory.
* @param options Optional parameters to delete this directory.
@ -110,6 +119,17 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
Azure::Core::Response<Models::DeleteDirectoryResult> Delete(
const DeleteDirectoryOptions& options = DeleteDirectoryOptions()) const;
/**
* @brief Deletes the directory if it exists.
* @param options Optional parameters to delete this directory.
* @return Azure::Core::Response<Models::DeleteDirectoryResult> containing the information
* returned when deleting the directory. Currently empty but preserved for future usage.
* Only when the delete operation if successful, the returned information other than 'Deleted'
* is valid.
*/
Azure::Core::Response<Models::DeleteDirectoryResult> DeleteIfExists(
const DeleteDirectoryOptions& options = DeleteDirectoryOptions()) const;
/**
* @brief Gets the properties of the directory.
* @param options Optional parameters to get this directory's properties.

View File

@ -94,6 +94,15 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
Azure::Core::Response<Models::DeleteFileResult> Delete(
const DeleteFileOptions& options = DeleteFileOptions()) const;
/**
* @brief Deletes the file if it exists.
* @param options Optional parameters to delete this file.
* @return Azure::Core::Response<DeleteFileResult> containing the information returned when
* deleting the file. Only valid when successfully deleted.
*/
Azure::Core::Response<Models::DeleteFileResult> DeleteIfExists(
const DeleteFileOptions& options = DeleteFileOptions()) const;
/**
* @brief Open a stream for the file's content, or a range of the file's content that can be
* used to download the server end data.

View File

@ -16,8 +16,17 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { names
using GetServicePropertiesResult = StorageServiceProperties;
// ShareClient models:
using CreateShareResult = ShareCreateResult;
using DeleteShareResult = ShareDeleteResult;
struct CreateShareResult
{
bool Created = true;
std::string ETag;
Core::DateTime LastModified;
};
struct DeleteShareResult
{
bool Deleted = true;
};
using CreateShareSnapshotResult = ShareCreateSnapshotResult;
using GetSharePropertiesResult = ShareGetPropertiesResult;
using SetShareQuotaResult = ShareSetQuotaResult;
@ -34,8 +43,26 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { names
using ChangeShareLeaseResult = ShareChangeLeaseResult;
// DirectoryClient models:
using CreateDirectoryResult = DirectoryCreateResult;
using DeleteDirectoryResult = DirectoryDeleteResult;
struct CreateDirectoryResult
{
bool Created = true;
std::string ETag;
Core::DateTime LastModified;
bool IsServerEncrypted = bool();
std::string FilePermissionKey;
std::string FileAttributes;
Core::DateTime FileCreatedOn;
Core::DateTime FileLastWrittenOn;
Core::DateTime FileChangedOn;
std::string FileId;
std::string FileParentId;
};
struct DeleteDirectoryResult
{
bool Deleted = true;
};
using GetDirectoryPropertiesResult = DirectoryGetPropertiesResult;
using SetDirectoryPropertiesResult = DirectorySetPropertiesResult;
using SetDirectoryMetadataResult = DirectorySetMetadataResult;
@ -65,6 +92,27 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { names
std::string ContinuationToken;
};
// FileClient models:
struct CreateFileResult
{
bool Created = true;
std::string ETag;
Core::DateTime LastModified;
bool IsServerEncrypted = bool();
std::string FilePermissionKey;
std::string FileAttributes;
Core::DateTime FileCreatedOn;
Core::DateTime FileLastWrittenOn;
Core::DateTime FileChangedOn;
std::string FileId;
std::string FileParentId;
};
struct DeleteFileResult
{
bool Deleted = true;
};
struct FileShareSmbProperties
{
/**
@ -90,9 +138,6 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { names
Azure::Core::Nullable<Core::DateTime> LastWrittenOn;
};
// FileClient models:
using CreateFileResult = FileCreateResult;
using DeleteFileResult = FileDeleteResult;
using DownloadFileResult = FileDownloadResult;
using StartCopyFileResult = FileStartCopyResult;
using AbortCopyFileResult = FileAbortCopyResult;

View File

@ -129,8 +129,34 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
auto protocolLayerOptions = Details::ShareRestClient::Share::CreateOptions();
protocolLayerOptions.Metadata = options.Metadata;
protocolLayerOptions.ShareQuota = options.ShareQuotaInGiB;
return Details::ShareRestClient::Share::Create(
auto result = Details::ShareRestClient::Share::Create(
m_shareUri, *m_pipeline, options.Context, protocolLayerOptions);
Models::CreateShareResult ret;
ret.Created = true;
ret.ETag = std::move(result->ETag);
ret.LastModified = std::move(result->LastModified);
return Azure::Core::Response<Models::CreateShareResult>(
std::move(ret), result.ExtractRawResponse());
}
Azure::Core::Response<Models::CreateShareResult> ShareClient::CreateIfNotExists(
const CreateShareOptions& options) const
{
try
{
return Create(options);
}
catch (StorageException& e)
{
if (e.ErrorCode == Details::ShareAlreadyExists)
{
Models::CreateShareResult ret;
ret.Created = false;
return Azure::Core::Response<Models::CreateShareResult>(
std::move(ret), std::move(e.RawResponse));
}
throw;
}
}
Azure::Core::Response<Models::DeleteShareResult> ShareClient::Delete(
@ -141,8 +167,32 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
{
protocolLayerOptions.XMsDeleteSnapshots = Models::DeleteSnapshotsOptionType::Include;
}
return Details::ShareRestClient::Share::Delete(
auto result = Details::ShareRestClient::Share::Delete(
m_shareUri, *m_pipeline, options.Context, protocolLayerOptions);
Models::DeleteShareResult ret;
ret.Deleted = true;
return Azure::Core::Response<Models::DeleteShareResult>(
std::move(ret), result.ExtractRawResponse());
}
Azure::Core::Response<Models::DeleteShareResult> ShareClient::DeleteIfExists(
const DeleteShareOptions& options) const
{
try
{
return Delete(options);
}
catch (StorageException& e)
{
if (e.ErrorCode == Details::ShareNotFound)
{
Models::DeleteShareResult ret;
ret.Deleted = false;
return Azure::Core::Response<Models::DeleteShareResult>(
std::move(ret), std::move(e.RawResponse));
}
throw;
}
}
Azure::Core::Response<Models::CreateShareSnapshotResult> ShareClient::CreateSnapshot(

View File

@ -167,16 +167,77 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
{
protocolLayerOptions.FilePermission = std::string(c_FileInheritPermission);
}
return Details::ShareRestClient::Directory::Create(
auto result = Details::ShareRestClient::Directory::Create(
m_shareDirectoryUri, *m_pipeline, options.Context, protocolLayerOptions);
Models::CreateDirectoryResult ret;
ret.Created = true;
ret.ETag = std::move(result->ETag);
ret.FileAttributes = result->FileAttributes;
ret.FileCreatedOn = std::move(result->FileCreatedOn);
ret.FileLastWrittenOn = std::move(result->FileLastWrittenOn);
ret.FilePermissionKey = std::move(result->FilePermissionKey);
ret.FileChangedOn = std::move(result->FileChangedOn);
ret.FileId = std::move(result->FileId);
ret.FileParentId = std::move(result->FileParentId);
ret.IsServerEncrypted = result->IsServerEncrypted;
ret.LastModified = std::move(result->LastModified);
return Azure::Core::Response<Models::CreateDirectoryResult>(
std::move(ret), result.ExtractRawResponse());
}
Azure::Core::Response<Models::CreateDirectoryResult> ShareDirectoryClient::CreateIfNotExists(
const CreateDirectoryOptions& options) const
{
try
{
return Create(options);
}
catch (StorageException& e)
{
if (e.ErrorCode == Details::ResourceAlreadyExists)
{
Models::CreateDirectoryResult ret;
ret.Created = false;
return Azure::Core::Response<Models::CreateDirectoryResult>(
std::move(ret), std::move(e.RawResponse));
}
throw;
}
}
Azure::Core::Response<Models::DeleteDirectoryResult> ShareDirectoryClient::Delete(
const DeleteDirectoryOptions& options) const
{
auto protocolLayerOptions = Details::ShareRestClient::Directory::DeleteOptions();
return Details::ShareRestClient::Directory::Delete(
auto result = Details::ShareRestClient::Directory::Delete(
m_shareDirectoryUri, *m_pipeline, options.Context, protocolLayerOptions);
Models::DeleteDirectoryResult ret;
ret.Deleted = true;
return Azure::Core::Response<Models::DeleteDirectoryResult>(
std::move(ret), result.ExtractRawResponse());
}
Azure::Core::Response<Models::DeleteDirectoryResult> ShareDirectoryClient::DeleteIfExists(
const DeleteDirectoryOptions& options) const
{
try
{
return Delete(options);
}
catch (StorageException& e)
{
if (e.ErrorCode == Details::ShareNotFound || e.ErrorCode == Details::ParentNotFound
|| e.ErrorCode == Details::ResourceNotFound)
{
Models::DeleteDirectoryResult ret;
ret.Deleted = false;
return Azure::Core::Response<Models::DeleteDirectoryResult>(
std::move(ret), std::move(e.RawResponse));
}
throw;
}
}
Azure::Core::Response<Models::GetDirectoryPropertiesResult> ShareDirectoryClient::GetProperties(
@ -266,7 +327,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
}
Azure::Core::Response<Models::ListDirectoryHandlesSinglePageResult>
ShareDirectoryClient::ListHandlesSinglePage(const ListDirectoryHandlesSinglePageOptions& options) const
ShareDirectoryClient::ListHandlesSinglePage(
const ListDirectoryHandlesSinglePageOptions& options) const
{
auto protocolLayerOptions = Details::ShareRestClient::Directory::ListHandlesOptions();
protocolLayerOptions.ContinuationToken = options.ContinuationToken;

View File

@ -185,8 +185,23 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
protocolLayerOptions.ContentMd5 = options.HttpHeaders.ContentHash;
}
protocolLayerOptions.LeaseIdOptional = options.AccessConditions.LeaseId;
return Details::ShareRestClient::File::Create(
auto result = Details::ShareRestClient::File::Create(
m_shareFileUri, *m_pipeline, options.Context, protocolLayerOptions);
Models::CreateFileResult ret;
ret.Created = true;
ret.ETag = std::move(result->ETag);
ret.FileAttributes = result->FileAttributes;
ret.FileCreatedOn = std::move(result->FileCreatedOn);
ret.FileLastWrittenOn = std::move(result->FileLastWrittenOn);
ret.FilePermissionKey = std::move(result->FilePermissionKey);
ret.FileChangedOn = std::move(result->FileChangedOn);
ret.FileId = std::move(result->FileId);
ret.FileParentId = std::move(result->FileParentId);
ret.IsServerEncrypted = result->IsServerEncrypted;
ret.LastModified = std::move(result->LastModified);
return Azure::Core::Response<Models::CreateFileResult>(
std::move(ret), result.ExtractRawResponse());
}
Azure::Core::Response<Models::DeleteFileResult> ShareFileClient::Delete(
@ -194,8 +209,33 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
{
auto protocolLayerOptions = Details::ShareRestClient::File::DeleteOptions();
protocolLayerOptions.LeaseIdOptional = options.AccessConditions.LeaseId;
return Details::ShareRestClient::File::Delete(
auto result = Details::ShareRestClient::File::Delete(
m_shareFileUri, *m_pipeline, options.Context, protocolLayerOptions);
Models::DeleteFileResult ret;
ret.Deleted = true;
return Azure::Core::Response<Models::DeleteFileResult>(
std::move(ret), result.ExtractRawResponse());
}
Azure::Core::Response<Models::DeleteFileResult> ShareFileClient::DeleteIfExists(
const DeleteFileOptions& options) const
{
try
{
return Delete(options);
}
catch (StorageException& e)
{
if (e.ErrorCode == Details::ShareNotFound || e.ErrorCode == Details::ParentNotFound
|| e.ErrorCode == Details::ResourceNotFound)
{
Models::DeleteFileResult ret;
ret.Deleted = false;
return Azure::Core::Response<Models::DeleteFileResult>(
std::move(ret), std::move(e.RawResponse));
}
throw;
}
}
Azure::Core::Response<Models::DownloadFileResult> ShareFileClient::Download(
@ -208,7 +248,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
{
protocolLayerOptions.Range = std::string("bytes=")
+ std::to_string(options.Range.GetValue().Offset) + std::string("-")
+ std::to_string(options.Range.GetValue().Offset + options.Range.GetValue().Length.GetValue() - 1);
+ std::to_string(options.Range.GetValue().Offset
+ options.Range.GetValue().Length.GetValue() - 1);
}
else
{
@ -472,12 +513,13 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
{
protocolLayerOptions.XMsRange = std::string("bytes=")
+ std::to_string(options.Range.GetValue().Offset) + std::string("-")
+ std::to_string(options.Range.GetValue().Offset + options.Range.GetValue().Length.GetValue() - 1);
+ std::to_string(options.Range.GetValue().Offset
+ options.Range.GetValue().Length.GetValue() - 1);
}
else
{
protocolLayerOptions.XMsRange
= std::string("bytes=") + std::to_string(options.Range.GetValue().Offset) + std::string("-");
protocolLayerOptions.XMsRange = std::string("bytes=")
+ std::to_string(options.Range.GetValue().Offset) + std::string("-");
}
}
@ -487,8 +529,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
m_shareFileUri, *m_pipeline, options.Context, protocolLayerOptions);
}
Azure::Core::Response<Models::ListFileHandlesSinglePageResult> ShareFileClient::ListHandlesSinglePage(
const ListFileHandlesSinglePageOptions& options) const
Azure::Core::Response<Models::ListFileHandlesSinglePageResult>
ShareFileClient::ListHandlesSinglePage(const ListFileHandlesSinglePageOptions& options) const
{
auto protocolLayerOptions = Details::ShareRestClient::File::ListHandlesOptions();
protocolLayerOptions.ContinuationToken = options.ContinuationToken;

View File

@ -72,6 +72,42 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_NO_THROW(client.Delete());
}
}
{
// CreateIfNotExists & DeleteIfExists.
{
auto client = Files::Shares::ShareClient::CreateFromConnectionString(
StandardStorageConnectionString(), LowercaseRandomString());
EXPECT_NO_THROW(client.Create());
EXPECT_NO_THROW(client.CreateIfNotExists());
EXPECT_NO_THROW(client.Delete());
EXPECT_NO_THROW(client.DeleteIfExists());
}
{
auto client = Files::Shares::ShareClient::CreateFromConnectionString(
StandardStorageConnectionString(), LowercaseRandomString());
EXPECT_NO_THROW(client.CreateIfNotExists());
EXPECT_THROW(client.Create(), StorageException);
EXPECT_NO_THROW(client.DeleteIfExists());
}
{
auto client = Files::Shares::ShareClient::CreateFromConnectionString(
StandardStorageConnectionString(), LowercaseRandomString());
auto created = client.Create()->Created;
EXPECT_TRUE(created);
auto createResult = client.CreateIfNotExists();
EXPECT_FALSE(createResult->Created);
EXPECT_TRUE(createResult->ETag.empty());
EXPECT_EQ(Core::DateTime(), createResult->LastModified);
auto deleted = client.Delete()->Deleted;
EXPECT_TRUE(deleted);
}
{
auto client = Files::Shares::ShareClient::CreateFromConnectionString(
StandardStorageConnectionString(), LowercaseRandomString());
auto deleteResult = client.DeleteIfExists();
EXPECT_FALSE(deleteResult->Deleted);
}
}
}
TEST_F(FileShareClientTest, ShareMetadata)

View File

@ -100,6 +100,57 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_THROW(client.Create(), StorageException);
}
}
{
// CreateIfNotExists & DeleteIfExists.
{
auto client = m_shareClient->GetRootShareDirectoryClient().GetSubShareDirectoryClient(
LowercaseRandomString());
EXPECT_NO_THROW(client.Create());
EXPECT_NO_THROW(client.CreateIfNotExists());
EXPECT_NO_THROW(client.Delete());
EXPECT_NO_THROW(client.DeleteIfExists());
}
{
auto client = m_shareClient->GetRootShareDirectoryClient().GetSubShareDirectoryClient(
LowercaseRandomString());
EXPECT_NO_THROW(client.CreateIfNotExists());
EXPECT_THROW(client.Create(), StorageException);
EXPECT_NO_THROW(client.DeleteIfExists());
}
{
auto client = m_shareClient->GetRootShareDirectoryClient().GetSubShareDirectoryClient(
LowercaseRandomString());
auto created = client.Create()->Created;
EXPECT_TRUE(created);
auto createResult = client.CreateIfNotExists();
EXPECT_FALSE(createResult->Created);
EXPECT_TRUE(createResult->ETag.empty());
EXPECT_EQ(Core::DateTime(), createResult->LastModified);
auto deleted = client.Delete()->Deleted;
EXPECT_TRUE(deleted);
}
{
auto client = m_shareClient->GetRootShareDirectoryClient().GetSubShareDirectoryClient(
LowercaseRandomString());
auto deleteResult = client.DeleteIfExists();
EXPECT_FALSE(deleteResult->Deleted);
}
{
auto shareClient = Files::Shares::ShareClient::CreateFromConnectionString(
StandardStorageConnectionString(), LowercaseRandomString());
auto client = shareClient.GetRootShareDirectoryClient().GetSubShareDirectoryClient(
LowercaseRandomString());
auto deleteResult = client.DeleteIfExists();
EXPECT_FALSE(deleteResult->Deleted);
}
{
auto client = m_shareClient->GetRootShareDirectoryClient()
.GetSubShareDirectoryClient(LowercaseRandomString())
.GetSubShareDirectoryClient(LowercaseRandomString());
auto deleteResult = client.DeleteIfExists();
EXPECT_FALSE(deleteResult->Deleted);
}
}
}
TEST_F(FileShareDirectoryClientTest, DirectoryMetadata)

View File

@ -69,6 +69,37 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_NO_THROW(client.Create(1024));
}
}
{
// DeleteIfExists.
{
auto client = m_shareClient->GetRootShareDirectoryClient().GetShareFileClient(
LowercaseRandomString());
EXPECT_NO_THROW(client.Create(1024));
EXPECT_NO_THROW(client.Delete());
EXPECT_NO_THROW(client.DeleteIfExists());
}
{
auto client = m_shareClient->GetRootShareDirectoryClient().GetShareFileClient(
LowercaseRandomString());
auto deleteResult = client.DeleteIfExists();
EXPECT_FALSE(deleteResult->Deleted);
}
{
auto shareClient = Files::Shares::ShareClient::CreateFromConnectionString(
StandardStorageConnectionString(), LowercaseRandomString());
auto client = m_shareClient->GetRootShareDirectoryClient().GetShareFileClient(
LowercaseRandomString());
auto deleteResult = client.DeleteIfExists();
EXPECT_FALSE(deleteResult->Deleted);
}
{
auto client = m_shareClient->GetRootShareDirectoryClient()
.GetSubShareDirectoryClient(LowercaseRandomString())
.GetShareFileClient(LowercaseRandomString());
auto deleteResult = client.DeleteIfExists();
EXPECT_FALSE(deleteResult->Deleted);
}
}
}
TEST_F(FileShareFileClientTest, FileMetadata)