From fc161f852351f5bdb45e4e5616c534c15bc11a5d Mon Sep 17 00:00:00 2001 From: Brandon Siegel <96068+bsiegel@users.noreply.github.com> Date: Thu, 3 Sep 2020 15:20:28 -0700 Subject: [PATCH] Update Tables README (#14698) * Update Tables README --- sdk/tables/azure-data-tables/README.md | 304 +++++++++++++++++- .../src/samples/java/ReadmeSamples.java | 167 ++++++++++ 2 files changed, 460 insertions(+), 11 deletions(-) create mode 100644 sdk/tables/azure-data-tables/src/samples/java/ReadmeSamples.java diff --git a/sdk/tables/azure-data-tables/README.md b/sdk/tables/azure-data-tables/README.md index a60e6c7ab5995..1e5415848b442 100644 --- a/sdk/tables/azure-data-tables/README.md +++ b/sdk/tables/azure-data-tables/README.md @@ -4,15 +4,10 @@ key/attribute store with a schemaless design. Tables storage gives developers fl best parts of Azure cloud. [Source code][source_code] | [Package (Maven)][package] | [API reference documentation][api_documentation] -| [Product documentation][azconfig_docs] | [Samples][samples] +| [Product documentation][product_documentation] | [Samples][samples] ## Getting started -### Prerequisites - -- Java Development Kit (JDK) with version 8 or above -- [Azure Subscription][azure_subscription] - ### Include the Package [//]: # ({x-version-update-start;com.azure:tables:azure-data-tables;current}) @@ -25,17 +20,296 @@ best parts of Azure cloud. ``` [//]: # ({x-version-update-end}) +### Prerequisites + +- [Java Development Kit (JDK)][jdk] with version 8 or above +- [Azure Subscription][azure_subscription] +- An existing Azure storage account or Azure Cosmos DB Table API account + +#### Create a Storage Account +To create a Storage Account you can use the [Azure Portal][storage_account_create_portal] or [Azure CLI][storage_account_create_cli]. + +```bash +az storage account create \ + --resource-group \ + --name \ + --location +``` + +Your storage account URL, subsequently identified as ``, would be formatted as follows +`http(s)://.table.core.windows.net`. + +#### Create a Cosmos DB Table API account +To create a Cosmos DB Table API account you can use the [Azure Portal][cosmosdb_create_portal] or [Azure CLI][cosmosdb_create_cli]. + +```bash +az cosmosdb create \ + --resource-group \ + --name \ + --capabilities EnableTable +``` + +Your Table API account URL, subsequently identified as ``, would be formatted as follows +`http(s)://.table.cosmosdb.azure.com`. + +### Authenticate the client +Every request made to the Table service must be authorized using a connection string, shared key credential, or shared access signature. The samples below demonstrate the usage of these methods. + +Note: Azure Tables doesn't support Azure Active Directory (AAD) authentication. + +#### Connection string +A connection string includes the authentication information required for your application to access data in an Azure table at runtime using Shared Key authorization. See [Authenticate with a connection string](#authenticate-with-a-connection-string) for an example of how to use a connection string with a `TableServiceClient`. + +You can obtain your connection string from the Azure Portal (click **Access keys** under **Settings** in the Portal Storage account blade, or **Connection String** under **Settings** in the Portal Cosmos DB account blade) or using the Azure CLI: + +```bash +# Storage account +az storage account show-connection-string \ + --resource-group \ + --name + +# Cosmos DB Table API account +az cosmosdb list-connection-strings \ + --resource-group \ + --name +``` + +#### Shared Key credential +Shared Key authorization relies on your account access keys and other parameters to produce an encrypted signature string that is passed on the request in the Authorization header. See [Authenticate with a Shared Key credential](#authenticate-with-a-shared-key-credential) for an example of how to use Shared Key authorization with a `TableServiceClient`. + +To use Shared Key authorization you'll need your account name and URL, as well as an account access key. You can obtain your primary access key from the Azure Portal (click **Access keys** under **Settings** in the Portal Storage account blade, or **Connection String** under **Settings** in the Portal Cosmos DB account blade) or using the Azure CLI: + +```bash +# Storage account +az storage account keys list \ + --resource-group \ + --account-name + +# Cosmos DB Table API account +az cosmosdb list-keys \ + --resource-group \ + --name +``` + +#### Shared Access Signature (SAS) +A shared access signature allows administrators to delegate granular access to an Azure table without sharing the access key directly. You can control what resources the client may access, what permissions it has on those resources, and how long the SAS is valid, among other parameters. It relies on your account access keys and other parameters to produce an encrypted signature string that is passed on the request in the query string. See [Authenticate with a Shared Access Signature (SAS) token](#authenticate-with-a-shared-access-signature-sas-token) for an example of how to use shared access signatures with a `TableServiceClient`. + +To use SAS token authorization you'll need your account name and URL, as well as the SAS. You can obtain your SAS from the Azure Portal (click **Shared access signature** under **Settings** in the Portal Storage account blade) or using the Azure CLI: + +```bash +# Account-level SAS +az storage account generate-sas \ + --account-name \ + --services t \ + --resource-types \ + --permissions \ + --expiry + +# Table-level SAS +az storage table generate-sas \ + --name +``` + ## Key concepts +- **TableServiceClient** - A `TableServiceClient` is a client object that enables you to interact with the Table Service in order to create, list, and delete tables. +- **TableClient** - A `TableClient` is a client object that enables you to interact with a specific table in order to create, update, list, and delete entities within it. +- **Table** - A table is a collection of entities. Tables don't enforce a schema on entities, which means a single table can contain entities that have different sets of properties. +- **Entity** - An entity is a set of properties, similar to a database row. An entity in Azure Storage can be up to 1MB in size. An entity in Azure Cosmos DB can be up to 2MB in size. An entity has a partition key and a row key which together uniquely identify the entity within the table. +- **Properties** - A property is a name-value pair. Each entity can include up to 252 properties to store data. Each entity also has three system properties that specify a partition key, a row key, and a timestamp. +- **Partition Key** - An entity's partition key identifies the partition within the table to which the entity belongs. Entities with the same partition key can be queried more quickly, and inserted/updated in atomic operations. +- **Row Key** - An entity's row key is its unique identifier within a partition. + +Common uses of the Table service include: + +- Storing TBs of structured data capable of serving web scale applications +- Storing datasets that don't require complex joins, foreign keys, or stored procedures and can be de-normalized for fast access +- Quickly querying data using a clustered index +- Accessing data using the OData protocol + ## Examples -Use the client library for Tables to: -- manages tables -- edit and access table contents + +- [Authenticate a client](#authenticate-a-client) + - [Authenticate with a connection string](#authenticate-with-a-connection-string) + - [Authenticate with a Shared Key](#authenticate-with-a-shared-key) + - [Authenticate with a Shared Access Signature (SAS)](#authenticate-with-a-shared-access-signature-sas) +- [Create, List, and Delete Azure tables](#create-list-and-delete-azure-tables) + - [Construct a `TableServiceClient`](#construct-a-tableserviceclient) + - [Create a table](#create-a-table) + - [List tables](#list-tables) + - [Delete a table](#delete-a-table) +- [Create, List, and Delete table entities](##create-list-and-delete-table-entities) + - [Construct a `TableClient`](#construct-a-tableclient) + - [Create an entity](#create-an-entity) + - [List entities](#list-entities) + - [Delete an entity](#delete-an-entity) + +### Authenticate a client + +#### Authenticate with a connection string +To use a connection string to authorize your client, call the builder's `connectionString` method with your connection string. + + +```java +TableServiceClient tableServiceClient = new TableServiceClientBuilder() + .connectionString("") + .buildClient(); +``` + +#### Authenticate with a Shared Key +To use a Shared Key to authorize your client, create an instance of `TablesSharedKeyCredential` with your account name and access key. Call the builder's `endpoint` method with your account URL and the `credential` method with the `TablesSharedKeyCredential` object you created. + + +```java +TablesSharedKeyCredential credential = new TablesSharedKeyCredential("", ""); +TableServiceClient tableServiceClient = new TableServiceClientBuilder() + .endpoint("") + .credential(credential) + .buildClient(); +``` + +#### Authenticate with a Shared Access Signature (SAS) +To use a SAS to authorize your client, call the builder's `endpoint` method with your account URL and the `sasToken` method with your SAS. + + +```java +TableServiceClient tableServiceClient = new TableServiceClientBuilder() + .endpoint("") + .sasToken("") + .buildClient(); +``` + +### Create, List, and Delete Azure tables + +#### Construct a `TableServiceClient` +Construct a `TableServiceClient` by creating an instance of `TableServiceClientBuilder` and then calling the builder's `buildClient` or `buildAsyncClient` methods. + + +```java +TableServiceClient tableServiceClient = new TableServiceClientBuilder() + .connectionString("") // or use any of the other authentication methods + .buildClient(); +``` + +#### Create a table +Create a table by calling the `TableServiceClient`'s `createTable` method. An exception will be thrown if a table with the provided name exists. + + +```java +tableServiceClient.createTable(tableName); +``` + +Alternatively, you can call the `createTableIfNotExists` method which will create the table only if no such table exists, and does not throw an exception. + + +```java +tableServiceClient.createTableIfNotExists(tableName); +``` + +#### List tables +List or query the set of existing tables by calling the `TableServiceClient`'s `listTables` method, optionally passing in a `ListTablesOptions` instance to filter or limit the query results. See [Supported Query Options][query_options] for details about supported query options. + + +```java +ListTablesOptions options = new ListTablesOptions() + .setFilter(String.format("TableName eq '%s'", tableName)); + +for (TableItem tableItem : tableServiceClient.listTables(options)) { + System.out.println(tableItem.getName()); +} +``` + +#### Delete a table +Delete a table by calling the `TableServiceClient`'s `deleteTable` method. An exception will be thrown if no table with the provided name exists. + + +```java +tableServiceClient.deleteTable(tableName); +``` + +### Create, List, and Delete table entities + +#### Construct a `TableClient` +Construct a `TableClient` by creating an instance of `TableClientBuilder`, calling the builder's `tableName` method with the name of the table, and then calling its `buildClient` or `buildAsyncClient` methods. + + +```java +TableClient tableClient = new TableClientBuilder() + .connectionString("") // or use any of the other authentication methods + .tableName(tableName) + .buildClient(); +``` + +Alternatively, a `TableClient` can be retrieved from an existing `TableServiceClient` by calling its `getTableClient` method. + + +```java +TableClient tableClient = tableServiceClient.getTableClient(tableName); +``` + +#### Create an entity +Create a new `TableEntity` instance, providing the partition key and row key of the entity to create, optionally adding properties to the created object. Then pass the object to the `TableClient`'s `createEntity` method. An exception will be thrown if an entity with the provided partition key and row key exists within the table. + + +```java +TableEntity entity = new TableEntity(partitionKey, rowKey) + .addProperty("Product", "Marker Set") + .addProperty("Price", 5.00) + .addProperty("Quantity", 21); + +tableClient.createEntity(entity); +``` + +#### List entities +List or query the set of entities within the table by calling the `TableClient`'s `listEntities` method, optionally passing in a `ListEntitiesOptions` instance to filter, select, or limit the query results. See [Supported Query Options][query_options] for details about supported query options. + + +```java +ListEntitiesOptions options = new ListEntitiesOptions() + .setFilter(String.format("PartitionKey eq '%s'", partitionKey)) + .setSelect("Product, Price"); + +for (TableEntity entity : tableClient.listEntities(options)) { + Map properties = entity.getProperties(); + System.out.println(String.format("%s: %.2f", properties.get("Product"), properties.get("Price"))); +} +``` + +#### Delete an entity +Delete an entity by calling the `TableClient`'s `deleteEntity` method. An exception will be thrown if no entity with the provided partition key and row key exists. + + +```java +tableClient.deleteEntity(partitionKey, rowKey); +``` ## Troubleshooting +### General +When you interact with Tables service using the Azure Tables library for Java, errors returned by the service correspond to the same HTTP status codes returned for [REST API][rest_api] requests. + +For example, if you try to create a table that already exists, a `409` error is returned, indicating "Conflict". + + +```java +// Create the table if it doesn't already exist. +tableServiceClient.createTableIfNotExists(tableName); + +// Now attempt to create the same table unconditionally. +try { + tableServiceClient.createTable(tableName); +} catch (TableStorageException e) { + System.out.println(e.getStatusCode()); // 409 +} +``` + +### Logging +Enabling logging may help uncover useful information about failures. In order to see a log of HTTP requests and responses, set the `AZURE_LOG_LEVEL` environment variable to the desired verbosity. See [LogLevel][log_level] for a description of available log levels. + ## Next steps +Get started with our [Table samples][samples]. + ## Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a [Contributor License Agreement (CLA)][cla] declaring that you have the right to, and actually do, grant us the rights to use your contribution. @@ -51,9 +325,17 @@ This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For m [coc_contact]: mailto:opencode@microsoft.com [coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ [coc]: https://opensource.microsoft.com/codeofconduct/ +[cosmosdb_create_cli]: https://docs.microsoft.com/azure/cosmos-db/scripts/cli/table/create +[cosmosdb_create_portal]: https://docs.microsoft.com/azure/cosmos-db/create-table-java#create-a-database-account +[jdk]: https://docs.microsoft.com/java/azure/jdk/ +[log_level]: https://github.com/Azure/azure-sdk-for-java/blob/master/sdk/core/azure-core/src/main/java/com/azure/core/util/logging/LogLevel.java [package]: https://search.maven.org/artifact/com.azure/azure-data-tables -[samples_readme]: src/samples/README.md +[product_documentation]: https://docs.microsoft.com/azure/cosmos-db/table-storage-overview +[query_options]: https://docs.microsoft.com/rest/api/storageservices/querying-tables-and-entities#supported-query-options +[rest_api]: https://docs.microsoft.com/rest/api/storageservices/table-service-rest-api [samples]: src/samples/java/ [source_code]: src +[storage_account_create_cli]: https://docs.microsoft.com/azure/storage/common/storage-account-create?tabs=azure-cli +[storage_account_create_portal]: https://docs.microsoft.com/azure/storage/common/storage-account-create?tabs=azure-portal -![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-java%2Fsdk%2Ftables%2Fazure-data-tables%2FREADME.png) \ No newline at end of file +![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-java%2Fsdk%2Ftables%2Fazure-data-tables%2FREADME.png) diff --git a/sdk/tables/azure-data-tables/src/samples/java/ReadmeSamples.java b/sdk/tables/azure-data-tables/src/samples/java/ReadmeSamples.java new file mode 100644 index 0000000000000..a2128783dcb1e --- /dev/null +++ b/sdk/tables/azure-data-tables/src/samples/java/ReadmeSamples.java @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.data.tables; + +import com.azure.data.tables.models.ListEntitiesOptions; +import com.azure.data.tables.models.ListTablesOptions; +import com.azure.data.tables.models.TableEntity; +import com.azure.data.tables.models.TableItem; +import com.azure.data.tables.models.TableStorageException; + +import java.util.Map; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS ARE USED TO EXTRACT + * APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING LINE NUMBERS OF EXISTING CODE + * SAMPLES. + * + * Class containing code snippets that will be injected to README.md. + */ +public class ReadmeSamples { + private final String partitionKey = ""; + private final String rowKey = ""; + private final String tableName = ""; + private final TableServiceClient tableServiceClient = new TableServiceClient(null); + private final TableClient tableClient = new TableClient(null); + private final TableEntity entity = new TableEntity(partitionKey, rowKey); + + /** + * Code sample for authenticating with a connection string. + */ + public void authenticateWithConnectionString() { + TableServiceClient tableServiceClient = new TableServiceClientBuilder() + .connectionString("") + .buildClient(); + } + + /** + * Code sample for authenticating with a shared key. + */ + public void authenticateWithSharedKey() { + TablesSharedKeyCredential credential = new TablesSharedKeyCredential("", ""); + TableServiceClient tableServiceClient = new TableServiceClientBuilder() + .endpoint("") + .credential(credential) + .buildClient(); + } + + /** + * Code sample for authenticating with a SAS. + */ + public void authenticateWithSAS() { + TableServiceClient tableServiceClient = new TableServiceClientBuilder() + .endpoint("") + .sasToken("") + .buildClient(); + } + + /** + * Code sample for constructing a service client. + */ + public void constructServiceClient() { + TableServiceClient tableServiceClient = new TableServiceClientBuilder() + .connectionString("") // or use any of the other authentication methods + .buildClient(); + } + + /** + * Code sample for creating a table. + * @throws TableStorageException if the table exists. + */ + public void createTable() { + tableServiceClient.createTable(tableName); + } + + /** + * Code sample for creating a table if it doesn't exist. + */ + public void createTableIfNotExists() { + tableServiceClient.createTableIfNotExists(tableName); + } + + /** + * Code sample for listing tables. + */ + public void listTables() { + ListTablesOptions options = new ListTablesOptions() + .setFilter(String.format("TableName eq '%s'", tableName)); + + for (TableItem tableItem : tableServiceClient.listTables(options)) { + System.out.println(tableItem.getName()); + } + } + + /** + * Code sample for deleting a table. + */ + public void deleteTable() { + tableServiceClient.deleteTable(tableName); + } + + /** + * Code sample for constructing a table client. + */ + public void constructTableClient() { + TableClient tableClient = new TableClientBuilder() + .connectionString("") // or use any of the other authentication methods + .tableName(tableName) + .buildClient(); + } + + /** + * Code sample for retrieving a table client from a service client. + */ + public void retrieveTableClient() { + TableClient tableClient = tableServiceClient.getTableClient(tableName); + } + + /** + * Code sample for creating an entity. + * @throws TableStorageException if the entity exists. + */ + public void createEntity() { + TableEntity entity = new TableEntity(partitionKey, rowKey) + .addProperty("Product", "Marker Set") + .addProperty("Price", 5.00) + .addProperty("Quantity", 21); + + tableClient.createEntity(entity); + } + + /** + * Code sample for listing entities. + */ + public void listEntities() { + ListEntitiesOptions options = new ListEntitiesOptions() + .setFilter(String.format("PartitionKey eq '%s'", partitionKey)) + .setSelect("Product, Price"); + + for (TableEntity entity : tableClient.listEntities(options)) { + Map properties = entity.getProperties(); + System.out.println(String.format("%s: %.2f", properties.get("Product"), properties.get("Price"))); + } + } + + /** + * Code sample for deleting an entity. + */ + public void deleteEntity() { + tableClient.deleteEntity(partitionKey, rowKey); + } + + /** + * Code sample for accessing information about an error. + */ + public void accessErrorInfo() { + // Create the table if it doesn't already exist. + tableServiceClient.createTableIfNotExists(tableName); + + // Now attempt to create the same table unconditionally. + try { + tableServiceClient.createTable(tableName); + } catch (TableStorageException e) { + System.out.println(e.getStatusCode()); // 409 + } + } +}