Skip to content

Commit c44cfc8

Browse files
authored
[Tables] Support bigint (Azure#15275)
* First stab at supporting bigint * Address PR comments * Fix lint issues * expose pipeline * Update CI and API Surface * Try excluding node8 * TestType filter * Display name filter * Remove variables * Keep jobMatrixFilter
1 parent 76015dc commit c44cfc8

File tree

10 files changed

+220
-29
lines changed

10 files changed

+220
-29
lines changed

sdk/tables/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ pr:
2323
include:
2424
- sdk/tables/
2525

26+
variables:
27+
jobMatrixFilter: ^((?!8x_node).)*$
28+
2629
extends:
2730
template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml
2831
parameters:

sdk/tables/data-tables/review/data-tables.api.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import { CommonClientOptions } from '@azure/core-client';
88
import { OperationOptions } from '@azure/core-client';
99
import { PagedAsyncIterableIterator } from '@azure/core-paging';
10+
import { Pipeline } from '@azure/core-rest-pipeline';
1011
import { PipelinePolicy } from '@azure/core-rest-pipeline';
1112

1213
// @public
@@ -69,6 +70,7 @@ export type GetStatisticsResponse = ServiceGetStatisticsHeaders & TableServiceSt
6970
// @public
7071
export type GetTableEntityOptions = OperationOptions & {
7172
queryOptions?: TableEntityQueryOptions;
73+
disableTypeConversion?: boolean;
7274
};
7375

7476
// @public
@@ -87,6 +89,7 @@ export const enum KnownGeoReplicationStatusType {
8789
// @public
8890
export type ListTableEntitiesOptions = OperationOptions & {
8991
queryOptions?: TableEntityQueryOptions;
92+
disableTypeConversion?: boolean;
9093
};
9194

9295
// @public
@@ -180,6 +183,7 @@ export class TableClient {
180183
getAccessPolicy(options?: OperationOptions): Promise<GetAccessPolicyResponse>;
181184
getEntity<T extends object = Record<string, unknown>>(partitionKey: string, rowKey: string, options?: GetTableEntityOptions): Promise<GetTableEntityResponse<TableEntityResult<T>>>;
182185
listEntities<T extends object = Record<string, unknown>>(options?: ListTableEntitiesOptions): PagedAsyncIterableIterator<TableEntityResult<T>, TableEntityResult<T>[]>;
186+
pipeline: Pipeline;
183187
setAccessPolicy(tableAcl: SignedIdentifier[], options?: OperationOptions): Promise<SetAccessPolicyResponse>;
184188
submitTransaction(actions: TransactionAction[]): Promise<TableTransactionResponse>;
185189
readonly tableName: string;
@@ -286,6 +290,7 @@ export class TableServiceClient {
286290
getProperties(options?: OperationOptions): Promise<GetPropertiesResponse>;
287291
getStatistics(options?: OperationOptions): Promise<GetStatisticsResponse>;
288292
listTables(options?: ListTableItemsOptions): PagedAsyncIterableIterator<TableItem, TableItem[]>;
293+
pipeline: Pipeline;
289294
setProperties(properties: ServiceProperties, options?: SetPropertiesOptions): Promise<SetPropertiesResponse>;
290295
url: string;
291296
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
/**
5+
* This sample demonstrates how to create and consume Int64 values using bigint
6+
*
7+
* @summary creates and works with an entity containing a bigint
8+
* @azsdk-weight 70
9+
*/
10+
11+
import { TableClient, TablesSharedKeyCredential } from "@azure/data-tables";
12+
// Load the .env file if it exists
13+
import * as dotenv from "dotenv";
14+
dotenv.config();
15+
16+
const tablesUrl = process.env["TABLES_URL"] || "";
17+
const accountName = process.env["ACCOUNT_NAME"] || "";
18+
const accountKey = process.env["ACCOUNT_KEY"] || "";
19+
20+
async function workingWithBigint() {
21+
console.log("Working with bigint sample");
22+
const client = new TableClient(
23+
tablesUrl,
24+
"testbigint",
25+
new TablesSharedKeyCredential(accountName, accountKey)
26+
);
27+
28+
await client.createTable();
29+
30+
type FooEntity = {
31+
foo: bigint;
32+
};
33+
34+
await client.createEntity<FooEntity>({ partitionKey: "p1", rowKey: "1", foo: BigInt("12345") });
35+
36+
const entity = await client.getEntity<FooEntity>("p1", "1");
37+
38+
// Do arithmetic operations with bigint
39+
const resultPlusOne = entity.foo + BigInt(1);
40+
41+
console.log(resultPlusOne);
42+
43+
await client.deleteTable();
44+
}
45+
46+
export async function main() {
47+
await workingWithBigint();
48+
}
49+
50+
main().catch((err) => {
51+
console.error("The sample encountered an error:", err);
52+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
/**
5+
* This sample demonstrates how to create and consume Int64 values
6+
*
7+
* @summary creates and works with an entity containing an Int64 value
8+
* @azsdk-weight 70
9+
*/
10+
11+
import { Edm, TableClient, TablesSharedKeyCredential } from "@azure/data-tables";
12+
// Load the .env file if it exists
13+
import * as dotenv from "dotenv";
14+
dotenv.config();
15+
16+
const tablesUrl = process.env["TABLES_URL"] || "";
17+
const accountName = process.env["ACCOUNT_NAME"] || "";
18+
const accountKey = process.env["ACCOUNT_KEY"] || "";
19+
20+
async function workingWithInt64() {
21+
console.log("working with Int64 sample");
22+
const client = new TableClient(
23+
tablesUrl,
24+
"testInt64",
25+
new TablesSharedKeyCredential(accountName, accountKey)
26+
);
27+
28+
await client.createTable();
29+
30+
type FooEntity = {
31+
foo: Edm<"Int64">;
32+
};
33+
34+
await client.createEntity<FooEntity>({
35+
partitionKey: "p1",
36+
rowKey: "1",
37+
// To work with Int64 we need to use an object that includes
38+
// the value as a string and a notation of the type, in this case Int64
39+
foo: { value: "12345", type: "Int64" }
40+
});
41+
42+
const entity = await client.getEntity<FooEntity>("p1", "1", { disableTypeConversion: true });
43+
44+
// In order to do arithmetic operations with Int64 you need to use
45+
// bigint or a third party library such as 'long'
46+
console.log(entity);
47+
48+
await client.deleteTable();
49+
}
50+
51+
export async function main() {
52+
await workingWithInt64();
53+
}
54+
55+
main().catch((err) => {
56+
console.error("The sample encountered an error:", err);
57+
});

sdk/tables/data-tables/src/TableClient.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import {
5252
} from "./utils/internalModels";
5353
import { Uuid } from "./utils/uuid";
5454
import { parseXML, stringifyXML } from "@azure/core-xml";
55+
import { Pipeline } from "@azure/core-rest-pipeline";
5556

5657
/**
5758
* A TableClient represents a Client to the Azure Tables service allowing you
@@ -62,6 +63,11 @@ export class TableClient {
6263
* Table Account URL
6364
*/
6465
public url: string;
66+
/**
67+
* Represents a pipeline for making a HTTP request to a URL.
68+
* Pipelines can have multiple policies to manage manipulating each request before and after it is made to the server.
69+
*/
70+
public pipeline: Pipeline;
6571
private table: Table;
6672
private credential: TablesSharedKeyCredentialLike | undefined;
6773
private interceptClient: TableClientLike | undefined;
@@ -183,6 +189,7 @@ export class TableClient {
183189
generatedClient.pipeline.addPolicy(tablesSharedKeyCredentialPolicy(credential));
184190
}
185191
this.table = generatedClient.table;
192+
this.pipeline = generatedClient.pipeline;
186193
}
187194

188195
/**
@@ -250,13 +257,16 @@ export class TableClient {
250257
}
251258

252259
try {
253-
const { queryOptions, ...getEntityOptions } = updatedOptions || {};
260+
const { disableTypeConversion, queryOptions, ...getEntityOptions } = updatedOptions || {};
254261
await this.table.queryEntitiesWithPartitionAndRowKey(this.tableName, partitionKey, rowKey, {
255262
...getEntityOptions,
256263
queryOptions: this.convertQueryOptions(queryOptions || {}),
257264
onResponse
258265
});
259-
const tableEntity = deserialize<TableEntityResult<T>>(parsedBody);
266+
const tableEntity = deserialize<TableEntityResult<T>>(
267+
parsedBody,
268+
disableTypeConversion ?? false
269+
);
260270

261271
return tableEntity;
262272
} catch (e) {
@@ -348,9 +358,10 @@ export class TableClient {
348358

349359
private async _listEntities<T extends object>(
350360
tableName: string,
351-
options?: InternalListTableEntitiesOptions
361+
options: InternalListTableEntitiesOptions = {}
352362
): Promise<ListEntitiesResponse<TableEntityResult<T>>> {
353-
const queryOptions = this.convertQueryOptions(options?.queryOptions || {});
363+
const { disableTypeConversion = false } = options;
364+
const queryOptions = this.convertQueryOptions(options.queryOptions || {});
354365
const {
355366
xMsContinuationNextPartitionKey: nextPartitionKey,
356367
xMsContinuationNextRowKey: nextRowKey,
@@ -360,7 +371,10 @@ export class TableClient {
360371
queryOptions
361372
});
362373

363-
const tableEntities = deserializeObjectsArray<TableEntityResult<T>>(value || []);
374+
const tableEntities = deserializeObjectsArray<TableEntityResult<T>>(
375+
value ?? [],
376+
disableTypeConversion
377+
);
364378

365379
return Object.assign([...tableEntities], {
366380
nextPartitionKey,
@@ -678,6 +692,12 @@ interface InternalListTableEntitiesOptions extends ListTableEntitiesOptions {
678692
* An entity query continuation token from a previous call.
679693
*/
680694
nextRowKey?: string;
695+
/**
696+
* If true, automatic type conversion will be disabled and entity properties will
697+
* be represented by full metadata types. For example, an Int32 value will be \{value: "123", type: "Int32"\} instead of 123.
698+
* This option applies for all the properties
699+
*/
700+
disableTypeConversion?: boolean;
681701
}
682702

683703
function isInternalClientOptions(options: any): options is InternalTransactionClientOptions {

sdk/tables/data-tables/src/TableServiceClient.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { createSpan } from "./utils/tracing";
2929
import { tablesSharedKeyCredentialPolicy } from "./TablesSharedKeyCredentialPolicy";
3030
import { parseXML, stringifyXML } from "@azure/core-xml";
3131
import { ListTableItemsResponse } from "./utils/internalModels";
32+
import { Pipeline } from "@azure/core-rest-pipeline";
3233

3334
/**
3435
* A TableServiceClient represents a Client to the Azure Tables service allowing you
@@ -39,6 +40,11 @@ export class TableServiceClient {
3940
* Table Account URL
4041
*/
4142
public url: string;
43+
/**
44+
* Represents a pipeline for making a HTTP request to a URL.
45+
* Pipelines can have multiple policies to manage manipulating each request before and after it is made to the server.
46+
*/
47+
public pipeline: Pipeline;
4248
private table: Table;
4349
private service: Service;
4450

@@ -132,6 +138,7 @@ export class TableServiceClient {
132138
if (credential) {
133139
client.pipeline.addPolicy(tablesSharedKeyCredentialPolicy(credential));
134140
}
141+
this.pipeline = client.pipeline;
135142
this.table = client.table;
136143
this.service = client.service;
137144
}

sdk/tables/data-tables/src/models.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ export type ListTableEntitiesOptions = OperationOptions & {
120120
* Query options group
121121
*/
122122
queryOptions?: TableEntityQueryOptions;
123+
/**
124+
* If true, automatic type conversion will be disabled and entity properties will
125+
* be represented by full metadata types. For example, an Int32 value will be \{value: "123", type: "Int32"\} instead of 123.
126+
* This option applies for all the properties
127+
*/
128+
disableTypeConversion?: boolean;
123129
};
124130

125131
/**
@@ -130,6 +136,12 @@ export type GetTableEntityOptions = OperationOptions & {
130136
* Parameter group
131137
*/
132138
queryOptions?: TableEntityQueryOptions;
139+
/**
140+
* If true, automatic type conversion will be disabled and entity properties will
141+
* be represented by full metadata types. For example, an Int32 value will be \{value: "123", type: "Int32"\} instead of 123.
142+
* This option applies for all the properties
143+
*/
144+
disableTypeConversion?: boolean;
133145
};
134146

135147
/**

sdk/tables/data-tables/src/serialization.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const Edm = {
2121
String: "Edm.String"
2222
} as const;
2323

24-
type supportedTypes = boolean | string | number | Date | Uint8Array;
24+
type supportedTypes = boolean | string | number | Date | Uint8Array | bigint;
2525

2626
type serializedType = {
2727
value: supportedTypes;
@@ -38,6 +38,9 @@ function serializePrimitive(value: any): serializedType {
3838
typeof value === "number"
3939
) {
4040
serializedValue.value = value;
41+
} else if (typeof value === "bigint") {
42+
serializedValue.value = value.toString();
43+
serializedValue.type = Edm.Int64;
4144
} else if (value instanceof Date) {
4245
serializedValue.value = value;
4346
serializedValue.type = Edm.DateTime;
@@ -105,42 +108,51 @@ export function serialize(obj: object): object {
105108
return serialized;
106109
}
107110

108-
function getTypedObject(value: any, type: string): any {
111+
function getTypedObject(value: any, type: string, disableTypeConversion: boolean): any {
109112
switch (type) {
110113
case Edm.Boolean:
114+
return disableTypeConversion ? { value, type: "Boolean" } : value;
111115
case Edm.Double:
116+
return disableTypeConversion ? { value, type: "Double" } : value;
112117
case Edm.Int32:
118+
return disableTypeConversion ? { value, type: "Int32" } : value;
113119
case Edm.String:
114-
return value;
120+
return disableTypeConversion ? { value, type: "String" } : value;
115121
case Edm.DateTime:
116-
return { value, type: "DateTime" };
122+
return disableTypeConversion ? { value, type: "DateTime" } : new Date(value);
117123
case Edm.Int64:
118-
return { value, type: "Int64" };
124+
return disableTypeConversion ? { value, type: "Int64" } : BigInt(value);
119125
case Edm.Guid:
120126
return { value, type: "Guid" };
121127
case Edm.Binary:
122-
return base64Decode(value);
128+
return disableTypeConversion ? { value, type: "Binary" } : base64Decode(value);
123129
default:
124130
throw new Error(`Unknown EDM type ${type}`);
125131
}
126132
}
127133

128-
export function deserialize<T extends object>(obj: object): T {
134+
export function deserialize<T extends object = Record<string, any>>(
135+
obj: object,
136+
disableTypeConversion: boolean = false
137+
): T {
129138
const deserialized: any = {};
130139
for (const [key, value] of Object.entries(obj)) {
131140
if (key.indexOf("@odata.type") === -1) {
132141
const transformedKey = propertyCaseMap.get(key) ?? key;
133142
let typedValue = value;
134143
if (`${key}@odata.type` in obj) {
135144
const type = (obj as any)[`${key}@odata.type`];
136-
typedValue = getTypedObject(value, type);
145+
typedValue = getTypedObject(value, type, disableTypeConversion);
137146
}
138147
deserialized[transformedKey] = typedValue;
139148
}
140149
}
141150
return deserialized;
142151
}
143152

144-
export function deserializeObjectsArray<T extends object>(objArray: object[]): T[] {
145-
return objArray.map((obj) => deserialize<T>(obj));
153+
export function deserializeObjectsArray<T extends object>(
154+
objArray: object[],
155+
disableTypeConversion: boolean
156+
): T[] {
157+
return objArray.map((obj) => deserialize<T>(obj, disableTypeConversion));
146158
}

0 commit comments

Comments
 (0)