Skip to content

Commit 12a5df3

Browse files
Merge pull request Azure#8 from seankane-msft/clarify-readme
Fixes Bad Request to more clear error
2 parents a60c5cd + adb71c2 commit 12a5df3

File tree

7 files changed

+87
-19
lines changed

7 files changed

+87
-19
lines changed

sdk/table/azure-table/README.md

+29-19
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ or [Azure CLI](https://docs.microsoft.com/azure/storage/common/storage-quickstar
3333
```bash
3434
# Create a new resource group to hold the storage account -
3535
# if using an existing resource group, skip this step
36-
az group create --name my-resource-group --location westus2
36+
az group create --name MyResourceGroup --location westus2
3737

3838
# Create the storage account
39-
az storage account create -n my-storage-account-name -g my-resource-group
39+
az storage account create -n mystorageaccount -g MyResourceGroup
4040
```
4141

4242
### Create the client
@@ -48,7 +48,7 @@ you to access the storage account:
4848
```python
4949
from azure.table import TableServiceClient
5050

51-
service = TableServiceClient(account_url="https://<my-storage-account-name>.table.core.windows.net/", credential=credential)
51+
service = TableServiceClient(account_url="https://<mystorageaccount>.table.core.windows.net/", credential=credential)
5252
```
5353

5454
#### Looking up the account URL
@@ -59,7 +59,7 @@ or [Azure CLI](https://docs.microsoft.com/cli/azure/storage/account?view=azure-c
5959

6060
```bash
6161
# Get the table service URL for the storage account
62-
az storage account show -n my-storage-account-name -g my-resource-group --query "primaryEndpoints.table"
62+
az storage account show -n mystorageaccount -g MyResourceGroup --query "primaryEndpoints.table"
6363
```
6464

6565
#### Types of credentials
@@ -89,7 +89,7 @@ The `credential` parameter may be provided in a number of different forms, depen
8989
(aka account key or access key), provide the key as a string. This can be found in the Azure Portal under the "Access Keys"
9090
section or by running the following Azure CLI command:
9191

92-
```az storage account keys list -g MyResourceGroup -n MyStorageAccount```
92+
```az storage account keys list -g MyResourceGroup -n mystorageaccount```
9393

9494
Use the key as the credential parameter to authenticate the client:
9595
```python
@@ -112,7 +112,7 @@ service = TableServiceClient.from_connection_string(conn_str=connection_string)
112112
The connection string to your storage account can be found in the Azure Portal under the "Access Keys" section or by running the following CLI command:
113113

114114
```bash
115-
az storage account show-connection-string -g MyResourceGroup -n MyStorageAccount
115+
az storage account show-connection-string -g MyResourceGroup -n mystorageaccount
116116
```
117117

118118
## Key concepts
@@ -158,16 +158,16 @@ Create a table in your storage account
158158
```python
159159
from azure.table import TableServiceClient
160160

161-
table = TableServiceClient.from_connection_string(conn_str="<connection_string>")
162-
table.create_table(table_name="myTable")
161+
table_service_client = TableServiceClient.from_connection_string(conn_str="<connection_string>")
162+
table_service_client.create_table(table_name="myTable")
163163
```
164164

165165
Use the async client to create a table
166166
```python
167167
from azure.table.aio import TableServiceClient
168168

169-
table = TableServiceClient.from_connection_string(conn_str="<connection_string>")
170-
await table.create_table(table_name="myTable")
169+
table_service_client = TableServiceClient.from_connection_string(conn_str="<connection_string>")
170+
await table_service_client.create_table(table_name="myTable")
171171
```
172172

173173
### Creating entities
@@ -178,19 +178,25 @@ from azure.table import TableClient
178178

179179
my_entity = {'PartitionKey':'part','RowKey':'row'}
180180

181-
table = TableClient.from_connection_string(conn_str="<connection_string>", table_name="my_table")
182-
entity = table.create_entity(table_entity_properties=my_entity)
181+
table_client = TableClient.from_connection_string(conn_str="<connection_string>", table_name="myTable")
182+
entity = table_client.create_entity(entity=my_entity)
183183
```
184184

185185
Create entities asynchronously
186186

187187
```python
188188
from azure.table.aio import TableClient
189189

190-
my_entity = {'PartitionKey':'part','RowKey':'row'}
190+
my_entity = {
191+
'PartitionKey': 'color',
192+
'RowKey': 'brand',
193+
'text': 'Marker',
194+
'color': 'Purple',
195+
'price': '5',
196+
}
191197

192-
table = TableClient.from_connection_string(conn_str="<connection_string>", table_name="my_table")
193-
entity = await table.create_entity(table_entity_properties=my_entity)
198+
table_client = TableClient.from_connection_string(conn_str="<connection_string>", table_name="mytable")
199+
entity = await table_client.create_entity(entity=my_entity)
194200
```
195201

196202
### Querying entities
@@ -199,17 +205,21 @@ Querying entities in the table
199205
```python
200206
from azure.table import TableClient
201207

202-
table = TableClient.from_connection_string(conn_str="<connection_string>", table_name="my_table")
203-
entity = table.query_entities(results_per_page=3)
208+
my_filter = "text eq Marker"
209+
210+
table_client = TableClient.from_connection_string(conn_str="<connection_string>", table_name="mytable")
211+
entity = table_client.query_entities(filter=my_filter)
204212
```
205213

206214
Querying entities asynchronously
207215

208216
```python
209217
from azure.table.aio import TableClient
210218

211-
table = TableClient.from_connection_string(conn_str="<connection_string>", table_name="my_table")
212-
entity = await table.query_entities(results_per_page=3)
219+
my_filter = "text eq Marker"
220+
221+
table_client = TableClient.from_connection_string(conn_str="<connection_string>", table_name="mytable")
222+
entity = await table_client.query_entities(filter=my_filter)
213223
```
214224

215225
## Optional Configuration

sdk/table/azure-table/azure/table/_shared/_error.py

+8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# license information.
55
# --------------------------------------------------------------------------
66
from sys import version_info
7+
from re import match
78

89
from azure.core.exceptions import HttpResponseError, ResourceExistsError, ResourceNotFoundError
910
from azure.table._shared.parser import _str
@@ -214,6 +215,13 @@ def _wrap_exception(ex, desired_type):
214215
return desired_type('{}: {}'.format(ex.__class__.__name__, msg))
215216

216217

218+
def _validate_table_name(table_name):
219+
if match("^[a-zA-Z]{1}[a-zA-Z0-9]{2,62}$", table_name) is None:
220+
raise ValueError(
221+
"Table names must be alphanumeric, cannot begin with a number, and must be between 3-63 characters long."
222+
)
223+
224+
217225
class AzureSigningError(Exception):
218226
"""
219227
Represents a fatal error when attempting to sign a request.

sdk/table/azure-table/azure/table/_table_client.py

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# --------------------------------------------------------------------------
66

77
import functools
8+
import re
89
from typing import Optional, Any
910

1011
try:
@@ -20,6 +21,7 @@
2021
from azure.table._generated.models import AccessPolicy, SignedIdentifier, TableProperties, QueryOptions
2122
from azure.table._serialize import _get_match_headers, _add_entity_properties
2223
from azure.table._shared.base_client import StorageAccountHostsMixin, parse_query, parse_connection_str
24+
from azure.table._shared._error import _validate_table_name
2325

2426
from azure.table._shared.request_handlers import serialize_iso
2527
from azure.table._shared.response_handlers import process_table_error
@@ -56,6 +58,8 @@ def __init__(
5658
:returns: None
5759
"""
5860

61+
_validate_table_name(table_name)
62+
5963
try:
6064
if not account_url.lower().startswith('http'):
6165
account_url = "https://" + account_url

sdk/table/azure-table/azure/table/_table_service_client.py

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# --------------------------------------------------------------------------
66

77
import functools
8+
import re
89
from typing import Any
910

1011
from azure.core.pipeline import Pipeline
@@ -26,6 +27,7 @@
2627
from azure.core.paging import ItemPaged
2728
from azure.core.tracing.decorator import distributed_trace
2829
from azure.table._table_client import TableClient
30+
from azure.table._shared._error import _validate_table_name
2931

3032

3133
class TableServiceClient(StorageAccountHostsMixin):
@@ -177,6 +179,8 @@ def create_table(
177179
:rtype: ~azure.table.TableClient
178180
:raises: ~azure.core.exceptions.HttpResponseError
179181
"""
182+
_validate_table_name(table_name)
183+
180184
table_properties = TableProperties(table_name=table_name, **kwargs)
181185
self._client.table.create(table_properties)
182186
table = self.get_table_client(table_name=table_name)
@@ -196,6 +200,8 @@ def delete_table(
196200
:return: None
197201
:rtype: None
198202
"""
203+
_validate_table_name(table_name)
204+
199205
self._client.table.delete(table=table_name, **kwargs)
200206

201207
@distributed_trace

sdk/table/azure-table/dev_requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
-e ../../../tools/azure-devtools
22
-e ../../../tools/azure-sdk-tools
3+
-e ../../identity/azure-identity
34
../../core/azure-core
45
cryptography>=2.1.4
56
aiohttp>=3.0; python_version >= '3.5'

sdk/table/azure-table/tests/test_table.py

+22
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,28 @@ def test_create_table_fail_on_exist(self, resource_group, location, storage_acco
116116
# self.assertEqual(existing[0], [table_name])
117117
ts.delete_table(table_name)
118118

119+
@GlobalStorageAccountPreparer()
120+
def test_create_table_invalid_name(self, resource_group, location, storage_account, storage_account_key):
121+
# Arrange
122+
ts = TableServiceClient(self.account_url(storage_account, "table"), storage_account_key)
123+
invalid_table_name = "my_table"
124+
125+
with pytest.raises(ValueError) as excinfo:
126+
ts.create_table(invalid_table_name)
127+
128+
assert "Table names must be alphanumeric, cannot begin with a number, and must be between 3-63 characters long.""" in str(excinfo)
129+
130+
@GlobalStorageAccountPreparer()
131+
def test_delete_table_invalid_name(self, resource_group, location, storage_account, storage_account_key):
132+
# Arrange
133+
ts = TableServiceClient(self.account_url(storage_account, "table"), storage_account_key)
134+
invalid_table_name = "my_table"
135+
136+
with pytest.raises(ValueError) as excinfo:
137+
ts.create_table(invalid_table_name)
138+
139+
assert "Table names must be alphanumeric, cannot begin with a number, and must be between 3-63 characters long.""" in str(excinfo)
140+
119141
# @pytest.mark.skip("pending")
120142
@GlobalStorageAccountPreparer()
121143
def test_query_tables(self, resource_group, location, storage_account, storage_account_key):

sdk/table/azure-table/tests/test_table_client.py

+17
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
# )
1818
from _shared.testcase import GlobalStorageAccountPreparer, TableTestCase
1919

20+
from azure.core.exceptions import HttpResponseError
2021
# ------------------------------------------------------------------------------
2122
SERVICES = {
2223
#TableServiceClient: 'table',
@@ -543,6 +544,22 @@ def test_create_table_client_with_complete_url(self, resource_group, location, s
543544
self.assertEqual(service.table_name, 'bar')
544545
self.assertEqual(service.account_name, storage_account.name)
545546

547+
# @pytest.mark.skip("pending")
548+
@GlobalStorageAccountPreparer()
549+
def test_create_table_client_with_invalid_name(self, resource_group, location, storage_account, storage_account_key):
550+
# Arrange
551+
table_url = "https://{}.table.core.windows.net:443/foo".format(storage_account.name)
552+
invalid_table_name = "my_table"
553+
554+
# Assert
555+
with pytest.raises(ValueError) as excinfo:
556+
service = TableClient(account_url=table_url, table_name=invalid_table_name, credential=storage_account_key)
557+
558+
assert "Table names must be alphanumeric, cannot begin with a number, and must be between 3-63 characters long.""" in str(excinfo)
559+
560+
# with self.assertRaises(ValueError):
561+
# service = TableClient(account_url=table_url, table_name=invalid_table_name, credential=storage_account_key)
562+
546563
#@pytest.mark.skip("pending")
547564
def test_error_with_malformed_conn_str(self):
548565
# Arrange

0 commit comments

Comments
 (0)