Skip to content

Commit 360c8f8

Browse files
jairmyreevcolin7
andauthored
[New API] Tables Sovereign Cloud Support (Azure#45246)
* Introducing TableAudience * Adding tests and updating TableAudience api * Update spacing * Add Javadoc for `getDefaultScope` * Fixing checkstyle violations * Adding license header * Removing necessary comment * Update sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClientBuilder.java Co-authored-by: vcolin7 <[email protected]> * Update sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableServiceClientBuilder.java Co-authored-by: vcolin7 <[email protected]> * Update sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableAudience.java Co-authored-by: vcolin7 <[email protected]> * Update sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableAudience.java Co-authored-by: vcolin7 <[email protected]> * Update sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableAudience.java Co-authored-by: vcolin7 <[email protected]> * Update sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableAudience.java Co-authored-by: vcolin7 <[email protected]> * Changes from feedback from Alan and Victor * Spotless updates * Fixing checkstyle violation --------- Co-authored-by: vcolin7 <[email protected]>
1 parent 9832929 commit 360c8f8

File tree

7 files changed

+193
-10
lines changed

7 files changed

+193
-10
lines changed

sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/BuilderHelper.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,9 @@
3434
import com.azure.data.tables.implementation.NullHttpClient;
3535
import com.azure.data.tables.implementation.StorageAuthenticationSettings;
3636
import com.azure.data.tables.implementation.StorageConnectionString;
37-
import com.azure.data.tables.implementation.StorageConstants;
3837
import com.azure.data.tables.implementation.TableBearerTokenChallengeAuthorizationPolicy;
3938
import com.azure.data.tables.implementation.TableUtils;
40-
import com.azure.data.tables.implementation.TablesConstants;
39+
import com.azure.data.tables.models.TableAudience;
4140

4241
import java.util.ArrayList;
4342
import java.util.List;
@@ -61,10 +60,12 @@ static HttpPipeline buildPipeline(AzureNamedKeyCredential azureNamedKeyCredentia
6160
RetryPolicy retryPolicy, RetryOptions retryOptions, HttpLogOptions logOptions, ClientOptions clientOptions,
6261
HttpClient httpClient, List<HttpPipelinePolicy> perCallAdditionalPolicies,
6362
List<HttpPipelinePolicy> perRetryAdditionalPolicies, Configuration configuration, ClientLogger logger,
64-
boolean enableTenantDiscovery) {
63+
boolean enableTenantDiscovery, TableAudience audience) {
6564
configuration = (configuration == null) ? Configuration.getGlobalConfiguration() : configuration;
6665
logOptions = (logOptions == null) ? new HttpLogOptions() : logOptions;
6766

67+
audience = (audience != null) ? audience : getDefaulTableAudience(TableUtils.isCosmosEndpoint(endpoint));
68+
6869
if (retryPolicy != null && retryOptions != null) {
6970
throw logger.logExceptionAsWarning(
7071
new IllegalStateException("'retryPolicy' and 'retryOptions' cannot both be set"));
@@ -115,7 +116,7 @@ static HttpPipeline buildPipeline(AzureNamedKeyCredential azureNamedKeyCredentia
115116
credentialPolicy = new AzureSasCredentialPolicy(new AzureSasCredential(sasToken), false);
116117
} else if (tokenCredential != null) {
117118
credentialPolicy = new TableBearerTokenChallengeAuthorizationPolicy(tokenCredential, enableTenantDiscovery,
118-
TableUtils.isCosmosEndpoint(endpoint) ? TablesConstants.COSMOS_SCOPE : StorageConstants.STORAGE_SCOPE);
119+
audience.getDefaultScope());
119120
} else {
120121
throw logger.logExceptionAsError(
121122
new IllegalStateException("A form of authentication is required to create a client. Use a builder's "
@@ -210,4 +211,8 @@ private static Tracer createTracer(ClientOptions clientOptions) {
210211
return TracerProvider.getDefaultProvider()
211212
.createTracer(CLIENT_NAME, CLIENT_VERSION, TABLES_TRACING_NAMESPACE_VALUE, tracingOptions);
212213
}
214+
215+
private static TableAudience getDefaulTableAudience(boolean isCosmosEndpoint) {
216+
return isCosmosEndpoint ? TableAudience.AZURE_COSMOS_PUBLIC_CLOUD : TableAudience.AZURE_STORAGE_PUBLIC_CLOUD;
217+
}
213218
}

sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClientBuilder.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.azure.data.tables.implementation.StorageEndpoint;
3232
import com.azure.data.tables.implementation.TablesJacksonSerializer;
3333
import com.azure.data.tables.implementation.TablesMultipartSerializer;
34+
import com.azure.data.tables.models.TableAudience;
3435

3536
import java.net.MalformedURLException;
3637
import java.net.URL;
@@ -194,6 +195,7 @@ public final class TableClientBuilder
194195
private RetryPolicy retryPolicy;
195196
private RetryOptions retryOptions;
196197
private boolean enableTenantDiscovery;
198+
private TableAudience audience;
197199

198200
/**
199201
* Creates a builder instance that is able to configure and construct {@link TableClient} and
@@ -273,7 +275,7 @@ public TableClient buildClient() {
273275
: BuilderHelper.buildPipeline(namedKeyCredential != null ? namedKeyCredential : azureNamedKeyCredential,
274276
azureSasCredential, tokenCredential, sasToken, endpoint, retryPolicy, retryOptions, httpLogOptions,
275277
clientOptions, httpClient, perCallPolicies, perRetryPolicies, configuration, logger,
276-
enableTenantDiscovery);
278+
enableTenantDiscovery, audience);
277279

278280
return new TableClient(tableName, pipeline, endpoint, serviceVersion, TABLES_SERIALIZER,
279281
TRANSACTIONAL_BATCH_SERIALIZER);
@@ -350,7 +352,7 @@ public TableAsyncClient buildAsyncClient() {
350352
: BuilderHelper.buildPipeline(namedKeyCredential != null ? namedKeyCredential : azureNamedKeyCredential,
351353
azureSasCredential, tokenCredential, sasToken, endpoint, retryPolicy, retryOptions, httpLogOptions,
352354
clientOptions, httpClient, perCallPolicies, perRetryPolicies, configuration, logger,
353-
enableTenantDiscovery);
355+
enableTenantDiscovery, audience);
354356

355357
return new TableAsyncClient(tableName, pipeline, endpoint, serviceVersion, TABLES_SERIALIZER,
356358
TRANSACTIONAL_BATCH_SERIALIZER);
@@ -744,4 +746,17 @@ public TableClientBuilder enableTenantDiscovery() {
744746

745747
return this;
746748
}
749+
750+
/**
751+
* Sets the {@link TableAudience} to use when authenticating with the Azure Tables service.
752+
*
753+
* @param audience The {@link TableAudience} to use when authenticating with the Table Service.
754+
*
755+
* @return The updated {@link TableClientBuilder}.
756+
*/
757+
public TableClientBuilder audience(TableAudience audience) {
758+
this.audience = audience;
759+
760+
return this;
761+
}
747762
}

sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableServiceClientBuilder.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.azure.data.tables.implementation.StorageAuthenticationSettings;
3131
import com.azure.data.tables.implementation.StorageConnectionString;
3232
import com.azure.data.tables.implementation.StorageEndpoint;
33+
import com.azure.data.tables.models.TableAudience;
3334

3435
import java.net.MalformedURLException;
3536
import java.net.URL;
@@ -184,6 +185,7 @@ public final class TableServiceClientBuilder implements TokenCredentialTrait<Tab
184185
private RetryPolicy retryPolicy;
185186
private RetryOptions retryOptions;
186187
private boolean enableTenantDiscovery;
188+
private TableAudience audience;
187189

188190
/**
189191
* Creates a builder instance that is able to configure and construct {@link TableServiceClient} and
@@ -286,7 +288,7 @@ private HttpPipeline prepareClient() {
286288
: BuilderHelper.buildPipeline(namedKeyCredential != null ? namedKeyCredential : azureNamedKeyCredential,
287289
azureSasCredential, tokenCredential, sasToken, endpoint, retryPolicy, retryOptions, httpLogOptions,
288290
clientOptions, httpClient, perCallPolicies, perRetryPolicies, configuration, logger,
289-
enableTenantDiscovery);
291+
enableTenantDiscovery, audience);
290292

291293
return pipeline;
292294
}
@@ -655,4 +657,16 @@ public TableServiceClientBuilder enableTenantDiscovery() {
655657

656658
return this;
657659
}
660+
661+
/**
662+
* Sets the {@link TableAudience audience} for the Azure Tables service.
663+
*
664+
* @param audience The {@link TableAudience audience} for the Azure Tables service.
665+
* @return The updated {@link TableServiceClientBuilder}.
666+
*/
667+
public TableServiceClientBuilder audience(TableAudience audience) {
668+
this.audience = audience;
669+
670+
return this;
671+
}
658672
}

sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/StorageConstants.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,6 @@ private StorageConstants() {
7272
*/
7373
public static final int BUFFER_COPY_LENGTH = 8 * KB;
7474

75-
public static final String STORAGE_SCOPE = "https://storage.azure.com/.default";
76-
7775
public static final String STORAGE_LOG_STRING_TO_SIGN = "Azure-Storage-Log-String-To-Sign";
7876

7977
/**

sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/TablesConstants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public final class TablesConstants {
7171
/**
7272
* Scope for Cosmos endpoints.
7373
*/
74-
public static final String COSMOS_SCOPE = "https://cosmos.azure.com/.default";
74+
public static final String DEFAULT_SCOPE = "/.default";
7575

7676
/**
7777
* Private constructor so this class cannot be instantiated.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
package com.azure.data.tables.models;
4+
5+
import com.azure.core.util.ExpandableStringEnum;
6+
import com.azure.core.util.logging.ClientLogger;
7+
8+
import java.net.URI;
9+
import java.net.URISyntaxException;
10+
11+
/**
12+
* Defines the audience for the Azure Tables service.
13+
* <p>
14+
* This class is used to specify the audience when creating clients.
15+
* <p>
16+
* The audience can be one of the following:
17+
* <ul>
18+
* <li>AZURE_STORAGE_PUBLIC_CLOUD</li>
19+
* <li>AZURE_STORAGE_CHINA</li>
20+
* <li>AZURE_STORAGE_US_GOVERNMENT</li>
21+
* <li>AZURE_COSMOS_PUBLIC_CLOUD</li>
22+
* <li>AZURE_COSMOS_CHINA</li>
23+
* <li>AZURE_COSMOS_US_GOVERNMENT</li>
24+
* </ul>
25+
*/
26+
public class TableAudience extends ExpandableStringEnum<TableAudience> {
27+
28+
/**
29+
* The audience for the Azure Storage service in the public cloud.
30+
*/
31+
public static final TableAudience AZURE_STORAGE_PUBLIC_CLOUD = fromString("https://storage.azure.com");
32+
33+
/**
34+
* The audience for the Azure Storage service in China.
35+
*/
36+
public static final TableAudience AZURE_STORAGE_CHINA = fromString("https://storage.azure.cn");
37+
38+
/**
39+
* The audience for the Azure Storage service in the US government cloud.
40+
*/
41+
public static final TableAudience AZURE_STORAGE_US_GOVERNMENT = fromString("https://storage.azure.us");
42+
43+
/**
44+
* The audience for the Azure Cosmos service in the public cloud.
45+
*/
46+
public static final TableAudience AZURE_COSMOS_PUBLIC_CLOUD = fromString("https://cosmos.azure.com");
47+
/**
48+
* The audience for the Azure Cosmos service in China.
49+
*/
50+
public static final TableAudience AZURE_COSMOS_CHINA = fromString("https://cosmos.azure.cn");
51+
/**
52+
* The audience for the Azure Cosmos service in the US government cloud.
53+
*/
54+
public static final TableAudience AZURE_COSMOS_US_GOVERNMENT = fromString("https://cosmos.azure.us");
55+
56+
private static final ClientLogger LOGGER = new ClientLogger(TableAudience.class);
57+
58+
/**
59+
* @deprecated The audience is for the public.
60+
*/
61+
@Deprecated
62+
TableAudience() {
63+
// This constructor is deprecated and should not be used.
64+
}
65+
66+
/**
67+
* Gets the default scope for the audience.
68+
* <p>
69+
* The default scope is the audience URI string with "/.default" appended to it.
70+
*
71+
* @return The default scope for the audience as a string.
72+
*/
73+
public String getDefaultScope() {
74+
try {
75+
URI uri = new URI(this.toString());
76+
String scheme = uri.getScheme();
77+
String host = uri.getHost();
78+
if (scheme != null && host != null) {
79+
return scheme + "://" + host + "/.default";
80+
} else {
81+
throw LOGGER.logExceptionAsError(new IllegalArgumentException("Invalid scope: " + this.toString()));
82+
}
83+
} catch (URISyntaxException e) {
84+
// Handle the exception
85+
throw LOGGER.logExceptionAsError(new RuntimeException("Invalid URI syntax: " + this.toString(), e));
86+
87+
}
88+
}
89+
90+
/**
91+
* Creates a new instance of TableAudience.
92+
*
93+
* @param audience The audience string.
94+
* @return A new instance of TableAudience.
95+
*/
96+
public static TableAudience fromString(String audience) {
97+
return fromString(audience, TableAudience.class);
98+
}
99+
100+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
package com.azure.data.tables;
4+
5+
import com.azure.data.tables.models.TableAudience;
6+
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.params.ParameterizedTest;
8+
import org.junit.jupiter.params.provider.CsvSource;
9+
10+
import static org.junit.jupiter.api.Assertions.assertEquals;
11+
12+
public class TableAudienceTests {
13+
14+
@Test
15+
public void testStorageAudiences() {
16+
TableAudience audience = TableAudience.AZURE_STORAGE_PUBLIC_CLOUD;
17+
assertEquals("https://storage.azure.com/.default", audience.getDefaultScope());
18+
19+
audience = TableAudience.AZURE_STORAGE_CHINA;
20+
assertEquals("https://storage.azure.cn/.default", audience.getDefaultScope());
21+
22+
audience = TableAudience.AZURE_STORAGE_US_GOVERNMENT;
23+
assertEquals("https://storage.azure.us/.default", audience.getDefaultScope());
24+
}
25+
26+
@Test
27+
public void testCosmosAudiences() {
28+
TableAudience audience = TableAudience.AZURE_COSMOS_PUBLIC_CLOUD;
29+
assertEquals("https://cosmos.azure.com/.default", audience.getDefaultScope());
30+
31+
audience = TableAudience.AZURE_COSMOS_CHINA;
32+
assertEquals("https://cosmos.azure.cn/.default", audience.getDefaultScope());
33+
34+
audience = TableAudience.AZURE_COSMOS_US_GOVERNMENT;
35+
assertEquals("https://cosmos.azure.us/.default", audience.getDefaultScope());
36+
}
37+
38+
@ParameterizedTest
39+
@CsvSource({
40+
"https://cosmos.azure.nz/, https://cosmos.azure.nz/.default",
41+
"https://cosmos.azure.nz, https://cosmos.azure.nz/.default",
42+
"https://cosmos.azure.nz/.default, https://cosmos.azure.nz/.default",
43+
"https://storage.azure.nz/, https://storage.azure.nz/.default",
44+
"https://storage.azure.nz, https://storage.azure.nz/.default",
45+
"https://storage.azure.nz/.default, https://storage.azure.nz/.default" })
46+
public void testCustomAudience(String customAudienceString, String expectedDefaultScope) {
47+
TableAudience audience = TableAudience.fromString(customAudienceString);
48+
assertEquals(expectedDefaultScope, audience.getDefaultScope());
49+
}
50+
51+
}

0 commit comments

Comments
 (0)