@azure/data-tables
Version:
An isomorphic client library for the Azure Tables service.
636 lines • 29.6 kB
JavaScript
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.TableClient = void 0;
const index_js_1 = require("./generated/index.js");
const core_auth_1 = require("@azure/core-auth");
const constants_js_1 = require("./utils/constants.js");
const continuationToken_js_1 = require("./utils/continuationToken.js");
const serialization_js_1 = require("./serialization.js");
const core_xml_1 = require("@azure/core-xml");
const TableTransaction_js_1 = require("./TableTransaction.js");
const uuid_js_1 = require("./utils/uuid.js");
const apiVersionPolicy_js_1 = require("./utils/apiVersionPolicy.js");
const cosmosPathPolicy_js_1 = require("./cosmosPathPolicy.js");
const odata_js_1 = require("./odata.js");
const connectionString_js_1 = require("./utils/connectionString.js");
const errorHelpers_js_1 = require("./utils/errorHelpers.js");
const isCosmosEndpoint_js_1 = require("./utils/isCosmosEndpoint.js");
const isCredential_js_1 = require("./utils/isCredential.js");
const logger_js_1 = require("./logger.js");
const challengeAuthenticationUtils_js_1 = require("./utils/challengeAuthenticationUtils.js");
const tablesNamedCredentialPolicy_js_1 = require("./tablesNamedCredentialPolicy.js");
const tablesSASTokenPolicy_js_1 = require("./tablesSASTokenPolicy.js");
const tracing_js_1 = require("./utils/tracing.js");
/**
* A TableClient represents a Client to the Azure Tables service allowing you
* to perform operations on a single table.
*/
class TableClient {
/**
* Table Account URL
*/
url;
/**
* Represents a pipeline for making a HTTP request to a URL.
* Pipelines can have multiple policies to manage manipulating each request before and after it is made to the server.
*/
pipeline;
table;
generatedClient;
credential;
clientOptions;
allowInsecureConnection;
/**
* Name of the table to perform operations on.
*/
tableName;
constructor(url, tableName, credentialOrOptions, options = {}) {
this.url = url;
this.tableName = tableName;
const isCosmos = (0, isCosmosEndpoint_js_1.isCosmosEndpoint)(this.url);
const credential = (0, isCredential_js_1.isCredential)(credentialOrOptions) ? credentialOrOptions : undefined;
this.credential = credential;
this.clientOptions = (!(0, isCredential_js_1.isCredential)(credentialOrOptions) ? credentialOrOptions : options) || {};
this.allowInsecureConnection = this.clientOptions.allowInsecureConnection ?? false;
const internalPipelineOptions = {
...this.clientOptions,
endpoint: this.clientOptions.endpoint || this.url,
loggingOptions: {
logger: logger_js_1.logger.info,
additionalAllowedHeaderNames: [...constants_js_1.TablesLoggingAllowedHeaderNames],
},
deserializationOptions: {
parseXML: core_xml_1.parseXML,
},
serializationOptions: {
stringifyXML: core_xml_1.stringifyXML,
},
};
const generatedClient = new index_js_1.GeneratedClient(this.url, internalPipelineOptions);
if ((0, core_auth_1.isNamedKeyCredential)(credential)) {
generatedClient.pipeline.addPolicy((0, tablesNamedCredentialPolicy_js_1.tablesNamedKeyCredentialPolicy)(credential));
}
else if ((0, core_auth_1.isSASCredential)(credential)) {
generatedClient.pipeline.addPolicy((0, tablesSASTokenPolicy_js_1.tablesSASTokenPolicy)(credential));
}
if ((0, core_auth_1.isTokenCredential)(credential)) {
const scope = isCosmos ? constants_js_1.COSMOS_SCOPE : constants_js_1.STORAGE_SCOPE;
(0, challengeAuthenticationUtils_js_1.setTokenChallengeAuthenticationPolicy)(generatedClient.pipeline, credential, scope);
}
if (isCosmos) {
generatedClient.pipeline.addPolicy((0, cosmosPathPolicy_js_1.cosmosPatchPolicy)());
}
if (options.version) {
generatedClient.pipeline.addPolicy((0, apiVersionPolicy_js_1.apiVersionPolicy)(options.version));
}
this.generatedClient = generatedClient;
this.table = generatedClient.table;
this.pipeline = generatedClient.pipeline;
}
/**
* Permanently deletes the current table with all of its entities.
* @param options - The options parameters.
*
* ### Example deleting a table
* ```ts snippet:ReadmeSampleDeleteTable
* import { DefaultAzureCredential } from "@azure/identity";
* import { TableClient } from "@azure/data-tables";
*
* const account = "<account>";
* const accountKey = "<accountkey>";
* const tableName = "<tableName>";
*
* const credential = new DefaultAzureCredential();
* const client = new TableClient(`https://${account}.table.core.windows.net`, tableName, credential);
*
* await client.deleteTable();
* ```
*/
deleteTable(options = {}) {
return tracing_js_1.tracingClient.withSpan("TableClient.deleteTable", options, async (updatedOptions) => {
try {
await this.table.delete(this.tableName, updatedOptions);
}
catch (e) {
if (e.statusCode === 404) {
logger_js_1.logger.info("TableClient.deleteTable: Table doesn't exist");
}
else {
throw e;
}
}
});
}
/**
* Creates a table with the tableName passed to the client constructor
* @param options - The options parameters.
*
* ### Example creating a table
* ```ts snippet:ReadmeSampleTableClientCreateTable
* import { DefaultAzureCredential } from "@azure/identity";
* import { TableClient } from "@azure/data-tables";
*
* const account = "<account>";
* const accountKey = "<accountkey>";
* const tableName = "<tableName>";
*
* const credential = new DefaultAzureCredential();
* const client = new TableClient(`https://${account}.table.core.windows.net`, tableName, credential);
*
* // If the table 'newTable' already exists, createTable doesn't throw
* await client.createTable();
* ```
*/
createTable(options = {}) {
return tracing_js_1.tracingClient.withSpan("TableClient.createTable", options, async (updatedOptions) => {
try {
await this.table.create({ name: this.tableName }, updatedOptions);
}
catch (e) {
(0, errorHelpers_js_1.handleTableAlreadyExists)(e, { ...updatedOptions, logger: logger_js_1.logger, tableName: this.tableName });
}
});
}
/**
* Returns a single entity in the table.
* @param partitionKey - The partition key of the entity.
* @param rowKey - The row key of the entity.
* @param options - The options parameters.
*
* ### Example getting an entity
* ```ts snippet:ReadmeSampleGetEntity
* import { DefaultAzureCredential } from "@azure/identity";
* import { TableClient } from "@azure/data-tables";
*
* const account = "<account>";
* const accountKey = "<accountkey>";
* const tableName = "<tableName>";
*
* const credential = new DefaultAzureCredential();
* const client = new TableClient(`https://${account}.table.core.windows.net`, tableName, credential);
*
* const entity = await client.getEntity("<partitionKey>", "<rowKey>");
* console.log(`Entity: PartitionKey: ${entity.partitionKey} RowKey: ${entity.rowKey}`);
* ```
*/
getEntity(partitionKey, rowKey,
// eslint-disable-next-line @azure/azure-sdk/ts-naming-options
options = {}) {
if (partitionKey === undefined || partitionKey === null) {
throw new Error("The entity's partitionKey cannot be undefined or null.");
}
if (rowKey === undefined || rowKey === null) {
throw new Error("The entity's rowKey cannot be undefined or null.");
}
return tracing_js_1.tracingClient.withSpan("TableClient.getEntity", options, async (updatedOptions) => {
let parsedBody;
function onResponse(rawResponse, flatResponse) {
parsedBody = rawResponse.parsedBody;
if (updatedOptions.onResponse) {
updatedOptions.onResponse(rawResponse, flatResponse);
}
}
const { disableTypeConversion, queryOptions, ...getEntityOptions } = updatedOptions;
await this.table.queryEntitiesWithPartitionAndRowKey(this.tableName, (0, odata_js_1.escapeQuotes)(partitionKey), (0, odata_js_1.escapeQuotes)(rowKey), {
...getEntityOptions,
queryOptions: (0, serialization_js_1.serializeQueryOptions)(queryOptions || {}),
onResponse,
});
const tableEntity = (0, serialization_js_1.deserialize)(parsedBody, disableTypeConversion ?? false);
return tableEntity;
});
}
/**
* Queries entities in a table.
* @param options - The options parameters.
*
* Example listing entities
* ```ts snippet:ReadmeSampleListEntities
* import { DefaultAzureCredential } from "@azure/identity";
* import { TableClient } from "@azure/data-tables";
*
* const account = "<account>";
* const accountKey = "<accountkey>";
* const tableName = "<tableName>";
*
* const credential = new DefaultAzureCredential();
* const client = new TableClient(`https://${account}.table.core.windows.net`, tableName, credential);
*
* let i = 0;
* const entities = client.listEntities();
* for await (const entity of entities) {
* console.log(`Entity${++i}: PartitionKey: ${entity.partitionKey} RowKey: ${entity.rowKey}`);
* }
* ```
*/
listEntities(
// eslint-disable-next-line @azure/azure-sdk/ts-naming-options
options = {}) {
const tableName = this.tableName;
const iter = this.listEntitiesAll(tableName, options);
return {
next() {
return iter.next();
},
[Symbol.asyncIterator]() {
return this;
},
byPage: (settings) => {
const pageOptions = {
...options,
queryOptions: { ...options.queryOptions, top: settings?.maxPageSize },
};
if (settings?.continuationToken) {
pageOptions.continuationToken = settings.continuationToken;
}
return this.listEntitiesPage(tableName, pageOptions);
},
};
}
async *listEntitiesAll(tableName, options) {
const firstPage = await this._listEntities(tableName, options);
yield* firstPage;
if (firstPage.continuationToken) {
const optionsWithContinuation = {
...options,
continuationToken: firstPage.continuationToken,
};
for await (const page of this.listEntitiesPage(tableName, optionsWithContinuation)) {
yield* page;
}
}
}
async *listEntitiesPage(tableName, options = {}) {
let result = await tracing_js_1.tracingClient.withSpan("TableClient.listEntitiesPage", options, (updatedOptions) => this._listEntities(tableName, updatedOptions));
yield result;
while (result.continuationToken) {
const optionsWithContinuation = {
...options,
continuationToken: result.continuationToken,
};
result = await tracing_js_1.tracingClient.withSpan("TableClient.listEntitiesPage", optionsWithContinuation, (updatedOptions, span) => {
span.setAttribute("continuationToken", result.continuationToken);
return this._listEntities(tableName, updatedOptions);
});
yield result;
}
}
async _listEntities(tableName, options = {}) {
const { disableTypeConversion = false } = options;
const queryOptions = (0, serialization_js_1.serializeQueryOptions)(options.queryOptions || {});
const listEntitiesOptions = {
...options,
queryOptions,
};
// If a continuation token is used, decode it and set the next row and partition key
if (options.continuationToken) {
const continuationToken = (0, continuationToken_js_1.decodeContinuationToken)(options.continuationToken);
listEntitiesOptions.nextRowKey = continuationToken.nextRowKey;
listEntitiesOptions.nextPartitionKey = continuationToken.nextPartitionKey;
}
const { xMsContinuationNextPartitionKey: nextPartitionKey, xMsContinuationNextRowKey: nextRowKey, value, } = await this.table.queryEntities(tableName, listEntitiesOptions);
const tableEntities = (0, serialization_js_1.deserializeObjectsArray)(value ?? [], disableTypeConversion);
// Encode nextPartitionKey and nextRowKey as a single continuation token and add it as a
// property to the page.
const continuationToken = (0, continuationToken_js_1.encodeContinuationToken)(nextPartitionKey, nextRowKey);
const page = Object.assign([...tableEntities], {
continuationToken,
});
return page;
}
/**
* Insert entity in the table.
* @param entity - The properties for the table entity.
* @param options - The options parameters.
*
* ### Example creating an entity
* ```ts snippet:ReadmeSampleCreateEntity
* import { DefaultAzureCredential } from "@azure/identity";
* import { TableClient } from "@azure/data-tables";
*
* const account = "<account>";
* const accountKey = "<accountkey>";
* const tableName = "<tableName>";
*
* const credential = new DefaultAzureCredential();
* const client = new TableClient(`https://${account}.table.core.windows.net`, tableName, credential);
*
* const testEntity = {
* partitionKey: "P1",
* rowKey: "R1",
* foo: "foo",
* bar: 123,
* };
* await client.createEntity(testEntity);
* ```
*/
createEntity(entity, options = {}) {
return tracing_js_1.tracingClient.withSpan("TableClient.createEntity", options, (updatedOptions) => {
const { ...createTableEntity } = updatedOptions || {};
return this.table.insertEntity(this.tableName, {
...createTableEntity,
tableEntityProperties: (0, serialization_js_1.serialize)(entity),
responsePreference: "return-no-content",
});
});
}
/**
* Deletes the specified entity in the table.
* @param partitionKey - The partition key of the entity.
* @param rowKey - The row key of the entity.
* @param options - The options parameters.
*
* ### Example deleting an entity
* ```ts snippet:ReadmeSampleDeleteEntity
* import { DefaultAzureCredential } from "@azure/identity";
* import { TableClient } from "@azure/data-tables";
*
* const account = "<account>";
* const accountKey = "<accountkey>";
* const tableName = "<tableName>";
*
* const credential = new DefaultAzureCredential();
* const client = new TableClient(`https://${account}.table.core.windows.net`, tableName, credential);
*
* // deleteEntity deletes the entity that matches exactly the partitionKey and rowKey
* await client.deleteEntity("<partitionKey>", "<rowKey>");
* ```
*/
deleteEntity(partitionKey, rowKey,
// eslint-disable-next-line @azure/azure-sdk/ts-naming-options
options = {}) {
if (partitionKey === undefined || partitionKey === null) {
throw new Error("The entity's partitionKey cannot be undefined or null.");
}
if (rowKey === undefined || rowKey === null) {
throw new Error("The entity's rowKey cannot be undefined or null.");
}
return tracing_js_1.tracingClient.withSpan("TableClient.deleteEntity", options, (updatedOptions) => {
const { etag = "*", ...rest } = updatedOptions;
const deleteOptions = {
...rest,
};
return this.table.deleteEntity(this.tableName, (0, odata_js_1.escapeQuotes)(partitionKey), (0, odata_js_1.escapeQuotes)(rowKey), etag, deleteOptions);
});
}
/**
* Update an entity in the table.
* @param entity - The properties of the entity to be updated.
* @param mode - The different modes for updating the entity:
* - Merge: Updates an entity by updating the entity's properties without replacing the existing entity.
* - Replace: Updates an existing entity by replacing the entire entity.
* @param options - The options parameters.
*
* ### Example updating an entity
* ```ts snippet:ReadmeSampleUpdateEntity
* import { DefaultAzureCredential } from "@azure/identity";
* import { TableClient } from "@azure/data-tables";
*
* const account = "<account>";
* const accountKey = "<accountkey>";
* const tableName = "<tableName>";
*
* const credential = new DefaultAzureCredential();
* const client = new TableClient(`https://${account}.table.core.windows.net`, tableName, credential);
*
* const entity = { partitionKey: "p1", rowKey: "r1", bar: "updatedBar" };
*
* // Update uses update mode "Merge" as default
* // merge means that update will match a stored entity
* // that has the same partitionKey and rowKey as the entity
* // passed to the method and then will only update the properties present in it.
* // Any other properties that are not defined in the entity passed to updateEntity
* // will remain as they are in the service
* await client.updateEntity(entity);
*
* // We can also set the update mode to Replace, which will match the entity passed
* // to updateEntity with one stored in the service and replace with the new one.
* // If there are any missing properties in the entity passed to updateEntity, they
* // will be removed from the entity stored in the service
* await client.updateEntity(entity, "Replace");
* ```
*/
updateEntity(entity, mode = "Merge",
// eslint-disable-next-line @azure/azure-sdk/ts-naming-options
options = {}) {
if (entity.partitionKey === undefined || entity.partitionKey === null) {
throw new Error("The entity's partitionKey cannot be undefined or null.");
}
if (entity.rowKey === undefined || entity.rowKey === null) {
throw new Error("The entity's rowKey cannot be undefined or null.");
}
return tracing_js_1.tracingClient.withSpan("TableClient.updateEntity", options, async (updatedOptions) => {
const partitionKey = (0, odata_js_1.escapeQuotes)(entity.partitionKey);
const rowKey = (0, odata_js_1.escapeQuotes)(entity.rowKey);
const { etag = "*", ...updateEntityOptions } = updatedOptions || {};
if (mode === "Merge") {
return this.table.mergeEntity(this.tableName, partitionKey, rowKey, {
tableEntityProperties: (0, serialization_js_1.serialize)(entity),
ifMatch: etag,
...updateEntityOptions,
});
}
if (mode === "Replace") {
return this.table.updateEntity(this.tableName, partitionKey, rowKey, {
tableEntityProperties: (0, serialization_js_1.serialize)(entity),
ifMatch: etag,
...updateEntityOptions,
});
}
throw new Error(`Unexpected value for update mode: ${mode}`);
}, {
spanAttributes: {
updateEntityMode: mode,
},
});
}
/**
* Upsert an entity in the table.
* @param entity - The properties for the table entity.
* @param mode - The different modes for updating the entity:
* - Merge: Updates an entity by updating the entity's properties without replacing the existing entity.
* - Replace: Updates an existing entity by replacing the entire entity.
* @param options - The options parameters.
*
* ### Example upserting an entity
* ```ts snippet:ReadmeSampleUpsertEntity
* import { DefaultAzureCredential } from "@azure/identity";
* import { TableClient } from "@azure/data-tables";
*
* const account = "<account>";
* const accountKey = "<accountkey>";
* const tableName = "<tableName>";
*
* const credential = new DefaultAzureCredential();
* const client = new TableClient(`https://${account}.table.core.windows.net`, tableName, credential);
*
* const entity = { partitionKey: "p1", rowKey: "r1", bar: "updatedBar" };
*
* // Upsert uses update mode "Merge" as default.
* // This behaves similarly to update but creates the entity
* // if it doesn't exist in the service
* await client.upsertEntity(entity);
*
* // We can also set the update mode to Replace.
* // This behaves similarly to update but creates the entity
* // if it doesn't exist in the service
* await client.upsertEntity(entity, "Replace");
* ```
*/
upsertEntity(entity, mode = "Merge", options = {}) {
return tracing_js_1.tracingClient.withSpan("TableClient.upsertEntity", options, async (updatedOptions) => {
if (entity.partitionKey === undefined || entity.partitionKey === null) {
throw new Error("The entity's partitionKey cannot be undefined or null.");
}
if (entity.rowKey === undefined || entity.rowKey === null) {
throw new Error("The entity's rowKey cannot be undefined or null.");
}
const partitionKey = (0, odata_js_1.escapeQuotes)(entity.partitionKey);
const rowKey = (0, odata_js_1.escapeQuotes)(entity.rowKey);
if (mode === "Merge") {
return this.table.mergeEntity(this.tableName, partitionKey, rowKey, {
tableEntityProperties: (0, serialization_js_1.serialize)(entity),
...updatedOptions,
});
}
if (mode === "Replace") {
return this.table.updateEntity(this.tableName, partitionKey, rowKey, {
tableEntityProperties: (0, serialization_js_1.serialize)(entity),
...updatedOptions,
});
}
throw new Error(`Unexpected value for update mode: ${mode}`);
}, {
spanAttributes: {
upsertEntityMode: mode,
},
});
}
/**
* Retrieves details about any stored access policies specified on the table that may be used with
* Shared Access Signatures.
* @param options - The options parameters.
*/
getAccessPolicy(options = {}) {
return tracing_js_1.tracingClient.withSpan("TableClient.getAccessPolicy", options, async (updatedOptions) => {
const signedIdentifiers = await this.table.getAccessPolicy(this.tableName, updatedOptions);
return (0, serialization_js_1.deserializeSignedIdentifier)(signedIdentifiers);
});
}
/**
* Sets stored access policies for the table that may be used with Shared Access Signatures.
* @param tableAcl - The Access Control List for the table.
* @param options - The options parameters.
*/
setAccessPolicy(tableAcl, options = {}) {
return tracing_js_1.tracingClient.withSpan("TableClient.setAccessPolicy", options, (updatedOptions) => {
const serlializedAcl = (0, serialization_js_1.serializeSignedIdentifiers)(tableAcl);
return this.table.setAccessPolicy(this.tableName, {
...updatedOptions,
tableAcl: serlializedAcl,
});
});
}
/**
* Submits a Transaction which is composed of a set of actions. You can provide the actions as a list
* or you can use {@link TableTransaction} to help building the transaction.
*
* Example usage:
* ```ts snippet:ReadmeSampleSubmitTransaction
* import { DefaultAzureCredential } from "@azure/identity";
* import { TableClient, TransactionAction } from "@azure/data-tables";
*
* const account = "<account>";
* const accountKey = "<accountkey>";
* const tableName = "<tableName>";
*
* const credential = new DefaultAzureCredential();
* const client = new TableClient(`https://${account}.table.core.windows.net`, tableName, credential);
*
* const actions: TransactionAction[] = [
* ["create", { partitionKey: "p1", rowKey: "1", data: "test1" }],
* ["delete", { partitionKey: "p1", rowKey: "2" }],
* ["update", { partitionKey: "p1", rowKey: "3", data: "newTest" }, "Merge"],
* ];
* const result = await client.submitTransaction(actions);
* ```
*
* Example usage with TableTransaction:
* ```ts snippet:ReadmeSampleSubmitTransactionWithTableTransaction
* import { DefaultAzureCredential } from "@azure/identity";
* import { TableClient, TableTransaction } from "@azure/data-tables";
*
* const account = "<account>";
* const accountKey = "<accountkey>";
* const tableName = "<tableName>";
*
* const credential = new DefaultAzureCredential();
* const client = new TableClient(`https://${account}.table.core.windows.net`, tableName, credential);
*
* const transaction = new TableTransaction();
*
* // Call the available action in the TableTransaction object
* transaction.createEntity({ partitionKey: "p1", rowKey: "1", data: "test1" });
* transaction.deleteEntity("p1", "2");
* transaction.updateEntity({ partitionKey: "p1", rowKey: "3", data: "newTest" }, "Merge");
*
* // submitTransaction with the actions list on the transaction.
* const result = await client.submitTransaction(transaction.actions);
* ```
*
* @param actions - tuple that contains the action to perform, and the entity to perform the action with
* @param options - Options for the request.
*/
async submitTransaction(actions, options = {}) {
const partitionKey = actions[0][1].partitionKey;
const transactionId = uuid_js_1.Uuid.generateUuid();
const changesetId = uuid_js_1.Uuid.generateUuid();
// Add pipeline
const transactionClient = new TableTransaction_js_1.InternalTableTransaction(this.url, partitionKey, transactionId, changesetId, this.generatedClient, new TableClient(this.url, this.tableName), this.credential, this.allowInsecureConnection);
for (const item of actions) {
const [action, entity, updateMode = "Merge", updateOptions] = item;
switch (action) {
case "create":
transactionClient.createEntity(entity);
break;
case "delete":
transactionClient.deleteEntity(entity.partitionKey, entity.rowKey);
break;
case "update":
transactionClient.updateEntity(entity, updateMode, updateOptions);
break;
case "upsert":
transactionClient.upsertEntity(entity, updateMode);
}
}
return transactionClient.submitTransaction(options);
}
/**
*
* Creates an instance of TableClient from connection string.
*
* @param connectionString - Account connection string or a SAS connection string of an Azure storage account.
* [ Note - Account connection string can only be used in NODE.JS runtime. ]
* Account connection string example -
* `DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=accountKey;EndpointSuffix=core.windows.net`
* SAS connection string example -
* `BlobEndpoint=https://myaccount.table.core.windows.net/;QueueEndpoint=https://myaccount.queue.core.windows.net/;FileEndpoint=https://myaccount.file.core.windows.net/;TableEndpoint=https://myaccount.table.core.windows.net/;SharedAccessSignature=sasString`
* @param options - Options to configure the HTTP pipeline.
* @returns A new TableClient from the given connection string.
*/
static fromConnectionString(connectionString, tableName,
// eslint-disable-next-line @azure/azure-sdk/ts-naming-options
options) {
const { url, options: clientOptions, credential, } = (0, connectionString_js_1.getClientParamsFromConnectionString)(connectionString, options);
if (credential) {
return new TableClient(url, tableName, credential, clientOptions);
}
else {
return new TableClient(url, tableName, clientOptions);
}
}
}
exports.TableClient = TableClient;
//# sourceMappingURL=TableClient.js.map