UNPKG

@azure/data-tables

Version:
636 lines • 29.6 kB
"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