Skip to content

Commit ff84ead

Browse files
authored
Fix ctor issue where Uri contains table name (Azure#24668)
1 parent 328f13d commit ff84ead

File tree

3 files changed

+94
-16
lines changed

3 files changed

+94
-16
lines changed

sdk/tables/Azure.Data.Tables/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
### Breaking Changes
88

99
### Bugs Fixed
10+
- Handle the case where the Uri parameter to the `TableClient` constructor contains the table name. ([#24667](https://github.com/Azure/azure-sdk-for-net/issues/24667))
1011

1112
### Other Changes
1213

sdk/tables/Azure.Data.Tables/src/TableClient.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ public virtual string AccountName
7373
/// </summary>
7474
/// <param name="endpoint">
7575
/// A <see cref="Uri"/> referencing the table service account.
76-
/// This is likely to be similar to "https://{account_name}.table.core.windows.net/{table_name}?{sas_token}" or
77-
/// "https://{account_name}.table.cosmos.azure.com/{table_name}?{sas_token}".
76+
/// This is likely to be similar to "https://{account_name}.table.core.windows.net/?{sas_token}" or
77+
/// "https://{account_name}.table.cosmos.azure.com?{sas_token}".
7878
/// </param>
7979
/// <param name="options">
8080
/// Optional client options that define the transport pipeline policies for authentication, retries, etc., that are applied to every request.
@@ -95,8 +95,8 @@ public TableClient(Uri endpoint, TableClientOptions options = null)
9595
/// </summary>
9696
/// <param name="endpoint">
9797
/// A <see cref="Uri"/> referencing the table service account.
98-
/// This is likely to be similar to "https://{account_name}.table.core.windows.net/{table_name}"
99-
/// or "https://{account_name}.table.cosmos.azure.com/{table_name}".
98+
/// This is likely to be similar to "https://{account_name}.table.core.windows.net"
99+
/// or "https://{account_name}.table.cosmos.azure.com".
100100
/// </param>
101101
/// <param name="credential">The shared access signature credential used to sign requests.</param>
102102
/// <param name="options">
@@ -227,8 +227,8 @@ public TableClient(string connectionString, string tableName, TableClientOptions
227227
/// </summary>
228228
/// <param name="endpoint">
229229
/// A <see cref="Uri"/> referencing the table service account.
230-
/// This is likely to be similar to "https://{account_name}.table.core.windows.net/{table_name}"
231-
/// or "https://{account_name}.table.cosmos.azure.com/{table_name}".
230+
/// This is likely to be similar to "https://{account_name}.table.core.windows.net"
231+
/// or "https://{account_name}.table.cosmos.azure.com".
232232
/// </param>
233233
/// <param name="tableName">The name of the table with which this client instance will interact.</param>
234234
/// <param name="tokenCredential">The <see cref="TokenCredential"/> used to authorize requests.</param>
@@ -251,7 +251,7 @@ public TableClient(Uri endpoint, string tableName, TokenCredential tokenCredenti
251251
Argument.AssertNotNullOrEmpty(tableName, nameof(tableName));
252252

253253
_endpoint = GetEndpointWithoutTableName(endpoint, tableName);
254-
_isCosmosEndpoint = TableServiceClient.IsPremiumEndpoint(endpoint);
254+
_isCosmosEndpoint = TableServiceClient.IsPremiumEndpoint(_endpoint);
255255
options ??= TableClientOptions.DefaultOptions;
256256

257257
var perCallPolicies = _isCosmosEndpoint ? new[] { new CosmosPatchTransformPolicy() } : Array.Empty<HttpPipelinePolicy>();
@@ -289,7 +289,7 @@ internal TableClient(Uri endpoint, string tableName, TableSharedKeyPipelinePolic
289289
Argument.AssertNotNullOrEmpty(tableName, nameof(tableName));
290290

291291
_endpoint = GetEndpointWithoutTableName(endpoint, tableName);
292-
_isCosmosEndpoint = TableServiceClient.IsPremiumEndpoint(endpoint);
292+
_isCosmosEndpoint = TableServiceClient.IsPremiumEndpoint(_endpoint);
293293
options ??= TableClientOptions.DefaultOptions;
294294

295295
var perCallPolicies = _isCosmosEndpoint ? new[] { new CosmosPatchTransformPolicy() } : Array.Empty<HttpPipelinePolicy>();

sdk/tables/Azure.Data.Tables/tests/TableClientTests.cs

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Net;
7+
using System.Threading;
78
using System.Threading.Tasks;
89
using Azure.Core;
910
using Azure.Core.TestFramework;
@@ -23,6 +24,7 @@ public TableClientTests(bool isAsync) : base(isAsync)
2324
private const string TableName = "someTableName";
2425
private const string AccountName = "someaccount";
2526
private static readonly Uri _url = new Uri($"https://someaccount.table.core.windows.net");
27+
private static readonly Uri _urlWithTableName = new Uri($"https://someaccount.table.core.windows.net/" + TableName);
2628
private readonly Uri _urlHttp = new Uri($"http://someaccount.table.core.windows.net");
2729
private MockTransport _transport;
2830
private TableClient client { get; set; }
@@ -105,14 +107,50 @@ public void ConstructorValidatesArguments()
105107

106108
public static IEnumerable<object[]> ValidConnStrings()
107109
{
108-
yield return new object[] { $"DefaultEndpointsProtocol=https;AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.com:443/;", AccountName, TableName };
109-
yield return new object[] { $"DefaultEndpointsProtocol=https;AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.microsoft.scloud:443/;", AccountName, TableName };
110-
yield return new object[] { $"DefaultEndpointsProtocol=https;AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.microsoft.scloud:443/{TableName};", AccountName, TableName };
111-
yield return new object[] { $"AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.com:443/;", AccountName, TableName };
112-
yield return new object[] { $"AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.microsoft.scloud:443/;", AccountName, TableName };
113-
yield return new object[] { $"AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.microsoft.scloud:443/;", AccountName, AccountName };
114-
yield return new object[] { $"AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.microsoft.scloud:443/{AccountName};", AccountName, AccountName };
115-
yield return new object[] { $"DefaultEndpointsProtocol=https;AccountName={AccountName};AccountKey={Secret};EndpointSuffix=core.windows.net", AccountName, TableName };
110+
yield return new object[]
111+
{
112+
$"DefaultEndpointsProtocol=https;AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.com:443/;",
113+
AccountName,
114+
TableName
115+
};
116+
yield return new object[]
117+
{
118+
$"DefaultEndpointsProtocol=https;AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.microsoft.scloud:443/;",
119+
AccountName,
120+
TableName
121+
};
122+
yield return new object[]
123+
{
124+
$"DefaultEndpointsProtocol=https;AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.microsoft.scloud:443/{TableName};",
125+
AccountName,
126+
TableName
127+
};
128+
yield return new object[]
129+
{
130+
$"AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.com:443/;", AccountName, TableName
131+
};
132+
yield return new object[]
133+
{
134+
$"AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.microsoft.scloud:443/;",
135+
AccountName,
136+
TableName
137+
};
138+
yield return new object[]
139+
{
140+
$"AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.microsoft.scloud:443/;",
141+
AccountName,
142+
AccountName
143+
};
144+
yield return new object[]
145+
{
146+
$"AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.microsoft.scloud:443/{AccountName};",
147+
AccountName,
148+
AccountName
149+
};
150+
yield return new object[]
151+
{
152+
$"DefaultEndpointsProtocol=https;AccountName={AccountName};AccountKey={Secret};EndpointSuffix=core.windows.net", AccountName, TableName
153+
};
116154
yield return new object[] { $"AccountName={AccountName};AccountKey={Secret};EndpointSuffix=core.windows.net", AccountName, TableName };
117155
yield return new object[] { $"DefaultEndpointsProtocol=https;AccountName={AccountName};AccountKey={Secret}", AccountName, TableName };
118156
yield return new object[] { $"AccountName={AccountName};AccountKey={Secret}", AccountName, TableName };
@@ -402,6 +440,37 @@ public void CreateIfNotExistsThrowsWhenTableBeingDeleted()
402440
Assert.ThrowsAsync<RequestFailedException>(() => client.CreateIfNotExistsAsync());
403441
}
404442

443+
private static IEnumerable<object[]> TableClientsWithTableNameInUri()
444+
{
445+
var tokenTransport = TableAlreadyExistsTransport();
446+
var sharedKeyTransport = TableAlreadyExistsTransport();
447+
var connStrTransport = TableAlreadyExistsTransport();
448+
var devTransport = TableAlreadyExistsTransport();
449+
450+
var sharedKeyClient = new TableClient(_url, TableName, new TableSharedKeyCredential(AccountName, Secret), new TableClientOptions { Transport = sharedKeyTransport });
451+
var connStringClient = new TableClient(
452+
$"DefaultEndpointsProtocol=https;AccountName={AccountName};AccountKey={Secret};TableEndpoint=https://{AccountName}.table.cosmos.azure.com:443/;",
453+
TableName,
454+
new TableClientOptions { Transport = connStrTransport });
455+
var devStorageClient = new TableClient("UseDevelopmentStorage=true", TableName, new TableClientOptions { Transport = devTransport });
456+
var tokenCredClient = new TableClient(_url, TableName, new MockCredential(), new TableClientOptions { Transport = tokenTransport });
457+
458+
yield return new object[] { sharedKeyClient, sharedKeyTransport };
459+
yield return new object[] { connStringClient, connStrTransport };
460+
yield return new object[] { devStorageClient, devTransport };
461+
yield return new object[] { tokenCredClient, tokenTransport };
462+
}
463+
464+
[TestCaseSource(nameof(TableClientsWithTableNameInUri))]
465+
public void CreateIfNotExistsDoesNotThrowWhenClientConstructedWithUriContainingTableName(TableClient tableClient, MockTransport transport)
466+
{
467+
client = InstrumentClient(tableClient);
468+
469+
client.CreateIfNotExistsAsync();
470+
471+
Assert.That(transport.SingleRequest.Uri.Path, Does.Not.Contain(TableName), "Path should not contain the table name");
472+
}
473+
405474
private static IEnumerable<object[]> TableClients()
406475
{
407476
var cred = new TableSharedKeyCredential(AccountName, Secret);
@@ -428,6 +497,14 @@ public void GenerateSasUri(TableClient client, TableSharedKeyCredential cred)
428497
Assert.AreEqual("?" + expectedSas, actualSas.Query);
429498
}
430499

500+
private static MockTransport TableAlreadyExistsTransport() =>
501+
new(
502+
_ => throw new RequestFailedException(
503+
(int)HttpStatusCode.Conflict,
504+
null,
505+
TableErrorCode.TableAlreadyExists.ToString(),
506+
null));
507+
431508
public class EnumEntity : ITableEntity
432509
{
433510
public string PartitionKey { get; set; }

0 commit comments

Comments
 (0)