diff --git a/sdk/storage/azfile/README.md b/sdk/storage/azfile/README.md index 013c2d022248..2b6ba2b9a02e 100644 --- a/sdk/storage/azfile/README.md +++ b/sdk/storage/azfile/README.md @@ -245,7 +245,7 @@ or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments. -[source]: https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/storage +[source]: https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/storage/azfile [docs]: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/storage/azfile [rest_docs]: https://docs.microsoft.com/rest/api/storageservices/file-service-rest-api [product_docs]: https://docs.microsoft.com/azure/storage/files/storage-files-introduction @@ -257,8 +257,8 @@ additional questions or comments. [storage_account_create_portal]: https://docs.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-portal [azure_sub]: https://azure.microsoft.com/free/ [azcore_response_error]: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azcore#ResponseError -[file_error]: https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/storage -[samples]: https://github.com/Azure/azure-sdk-for-go/blob/main/sdk/storage +[file_error]: https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/storage/azfile/fileerror/error_codes.go +[samples]: https://github.com/Azure/azure-sdk-for-go/blob/main/sdk/storage/azfile/file/examples_test.go [storage_contrib]: https://github.com/Azure/azure-sdk-for-go/blob/main/CONTRIBUTING.md [cla]: https://cla.microsoft.com [coc]: https://opensource.microsoft.com/codeofconduct/ diff --git a/sdk/storage/azfile/file/client_test.go b/sdk/storage/azfile/file/client_test.go index f04191104e1a..094b660b9d7b 100644 --- a/sdk/storage/azfile/file/client_test.go +++ b/sdk/storage/azfile/file/client_test.go @@ -3117,5 +3117,3 @@ func (f *FileRecordedTestsSuite) TestFileForceCloseHandlesDefault() { } // TODO: Add tests for retry header options - -// TODO: fix links in README: source, file_error, samples diff --git a/sdk/storage/azfile/file/models.go b/sdk/storage/azfile/file/models.go index f27195800f02..d8792ba6f98b 100644 --- a/sdk/storage/azfile/file/models.go +++ b/sdk/storage/azfile/file/models.go @@ -732,7 +732,7 @@ func (u *UploadStreamOptions) getUploadRangeOptions() *UploadRangeOptions { } } -// URLParts object represents the components that make up an Azure Storage Container/Blob URL. +// URLParts object represents the components that make up an Azure Storage Share/Directory/File URL. // NOTE: Changing any SAS-related field requires computing a new SAS signature. type URLParts = sas.URLParts diff --git a/sdk/storage/azfile/internal/shared/shared.go b/sdk/storage/azfile/internal/shared/shared.go index 9ef2a3396816..0b819c28ea5a 100644 --- a/sdk/storage/azfile/internal/shared/shared.go +++ b/sdk/storage/azfile/internal/shared/shared.go @@ -100,20 +100,37 @@ func ParseConnectionString(connectionString string) (ParsedConnectionString, err connStrMap[parts[0]] = parts[1] } - accountName, ok := connStrMap["AccountName"] - if !ok { - return ParsedConnectionString{}, errors.New("connection string missing AccountName") - } - + accountName := connStrMap["AccountName"] accountKey, ok := connStrMap["AccountKey"] if !ok { sharedAccessSignature, ok := connStrMap["SharedAccessSignature"] if !ok { return ParsedConnectionString{}, errors.New("connection string missing AccountKey and SharedAccessSignature") } - return ParsedConnectionString{ - ServiceURL: fmt.Sprintf("%v://%v.file.%v/?%v", defaultScheme, accountName, defaultSuffix, sharedAccessSignature), - }, nil + + fileEndpoint, ok := connStrMap["FileEndpoint"] + if !ok { + // We don't have a FileEndpoint, assume the default + if accountName != "" { + return ParsedConnectionString{ + ServiceURL: fmt.Sprintf("%v://%v.file.%v/?%v", defaultScheme, accountName, defaultSuffix, sharedAccessSignature), + }, nil + } else { + return ParsedConnectionString{}, errors.New("connection string missing AccountName") + } + } else { + if !strings.HasSuffix(fileEndpoint, "/") { + // add a trailing slash to be consistent with the portal + fileEndpoint += "/" + } + return ParsedConnectionString{ + ServiceURL: fmt.Sprintf("%v?%v", fileEndpoint, sharedAccessSignature), + }, nil + } + } else { + if accountName == "" { + return ParsedConnectionString{}, errors.New("connection string missing AccountName") + } } protocol, ok := connStrMap["DefaultEndpointsProtocol"] diff --git a/sdk/storage/azfile/internal/shared/shared_test.go b/sdk/storage/azfile/internal/shared/shared_test.go index 1cd5da99469d..8bb7f9a9e655 100644 --- a/sdk/storage/azfile/internal/shared/shared_test.go +++ b/sdk/storage/azfile/internal/shared/shared_test.go @@ -7,6 +7,7 @@ package shared import ( + "fmt" "testing" "github.com/stretchr/testify/require" @@ -93,3 +94,42 @@ func TestCParseConnectionStringAzurite(t *testing.T) { require.Equal(t, "dummyaccountname", parsed.AccountName) require.Equal(t, "secretkeykey", parsed.AccountKey) } + +func TestParseConnectionStringSASAndCustomDomain(t *testing.T) { + testData := []struct { + connectionStr string + parsedServiceURL string + parsedAccountName string + parsedAccountKey string + err error + }{ + { + connectionStr: "AccountName=dummyaccountname;SharedAccessSignature=fakesharedaccesssignature;FileEndpoint=http://127.0.0.1:10000/dummyaccountname;", + parsedServiceURL: "http://127.0.0.1:10000/dummyaccountname/?fakesharedaccesssignature", + }, + { + connectionStr: "BlobEndpoint=https://dummyaccountname.blob.core.windows.net/;FileEndpoint=https://dummyaccountname.file.core.windows.net/;SharedAccessSignature=fakesharedaccesssignature", + parsedServiceURL: "https://dummyaccountname.file.core.windows.net/?fakesharedaccesssignature", + }, + { + connectionStr: "BlobEndpoint=https://dummyaccountname.blob.core.windows.net;FileEndpoint=https://dummyaccountname.file.core.windows.net;SharedAccessSignature=fakesharedaccesssignature", + parsedServiceURL: "https://dummyaccountname.file.core.windows.net/?fakesharedaccesssignature", + }, + { + connectionStr: "SharedAccessSignature=fakesharedaccesssignature", + err: fmt.Errorf("connection string missing AccountName"), + }, + { + connectionStr: "DefaultEndpointsProtocol=http;AccountKey=secretkeykey;EndpointSuffix=core.windows.net", + err: fmt.Errorf("connection string missing AccountName"), + }, + } + + for _, td := range testData { + parsed, err := ParseConnectionString(td.connectionStr) + require.Equal(t, td.err, err) + require.Equal(t, td.parsedServiceURL, parsed.ServiceURL) + require.Equal(t, td.parsedAccountName, parsed.AccountName) + require.Equal(t, td.parsedAccountKey, parsed.AccountKey) + } +}