diff --git a/sdk/cosmosdb/cosmos/.eslintignore b/sdk/cosmosdb/cosmos/.eslintignore new file mode 100644 index 000000000000..c67e63baa949 --- /dev/null +++ b/sdk/cosmosdb/cosmos/.eslintignore @@ -0,0 +1 @@ +**/src/utils/hashing/murmurHash.ts \ No newline at end of file diff --git a/sdk/cosmosdb/cosmos/.eslintrc.json b/sdk/cosmosdb/cosmos/.eslintrc.json index e67e1b7581b9..189e66aa5366 100644 --- a/sdk/cosmosdb/cosmos/.eslintrc.json +++ b/sdk/cosmosdb/cosmos/.eslintrc.json @@ -11,6 +11,15 @@ "some-rule": "off", "@azure/azure-sdk/ts-package-json-module": "warn", "@azure/azure-sdk/ts-package-json-types": "warn", + // cosmos does not use core-http yet so their methods do not yet accept abort controllers + "@azure/azure-sdk/ts-apisurface-supportcancellation": "warn", + // this rule requires introducing breaking changes, should be fixed by the cosmos team + "@azure/azure-sdk/ts-naming-options": "warn", + // left to the cosmos team make this rule error again + "@azure/azure-sdk/ts-package-json-module": "warn", + "@azure/azure-sdk/ts-doc-internal": "warn", + // https://github.com/benmosher/eslint-plugin-import/issues/1816 + "import/no-extraneous-dependencies": "off", "@typescript-eslint/tslint/config": [ "error", { diff --git a/sdk/cosmosdb/cosmos/.vscode/settings.json b/sdk/cosmosdb/cosmos/.vscode/settings.json new file mode 100644 index 000000000000..3b664107303d --- /dev/null +++ b/sdk/cosmosdb/cosmos/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "git.ignoreLimitWarning": true +} \ No newline at end of file diff --git a/sdk/cosmosdb/cosmos/api-extractor.json b/sdk/cosmosdb/cosmos/api-extractor.json index 7ac6a6ec9fea..bffe897936d7 100644 --- a/sdk/cosmosdb/cosmos/api-extractor.json +++ b/sdk/cosmosdb/cosmos/api-extractor.json @@ -11,7 +11,7 @@ "dtsRollup": { "enabled": true, "untrimmedFilePath": "", - "publicTrimmedFilePath": "./dist/types/latest/index.d.ts" + "publicTrimmedFilePath": "./dist/types/latest/cosmos.d.ts" }, "messages": { "tsdocMessageReporting": { diff --git a/sdk/cosmosdb/cosmos/package.json b/sdk/cosmosdb/cosmos/package.json index fc61063cb8d1..172b6e682bfa 100644 --- a/sdk/cosmosdb/cosmos/package.json +++ b/sdk/cosmosdb/cosmos/package.json @@ -32,13 +32,13 @@ "README.md", "LICENSE" ], - "homepage": "https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/cosmosdb/cosmos/", + "homepage": "https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/cosmosdb/cosmos/README.md", "sideEffects": false, - "types": "./dist/types/latest/index.d.ts", + "types": "./dist/types/latest/cosmos.d.ts", "typesVersions": { "<3.6": { "*": [ - "./dist/types/3.1/index.d.ts" + "./dist/types/3.1/cosmos.d.ts" ] } }, @@ -64,8 +64,8 @@ "integration-test:browser": "echo skipped", "integration-test:node": "mocha -r test/mocha.env.ts -r ts-node/register -r esm -r dotenv/config -r ./test/public/common/setup.ts \"./test/internal/**/*.spec.ts\" \"./test/public/**/*.spec.ts\" --timeout 100000", "integration-test": "npm run integration-test:node && npm run integration-test:browser", - "lint:fix": "eslint package.json src test samples --ext .ts --fix --fix-type [problem,suggestion]", - "lint": "eslint package.json src test samples --ext .ts -f html -o cosmos-lintReport.html || exit 0", + "lint:fix": "eslint package.json api-extractor.json src test samples --ext .ts --fix --fix-type [problem,suggestion]", + "lint": "eslint package.json api-extractor.json src samples --ext .ts", "pack": "npm pack 2>&1", "prebuild": "npm run clean", "test:browser": "npm run unit-test:browser && npm run integration-test:browser", @@ -86,7 +86,7 @@ "tsdocFlavor": "AEDoc" }, "dependencies": { - "@azure/identity": "^1.1.0", + "@azure/core-auth": "^1.2.0", "@types/debug": "^4.1.4", "debug": "^4.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -100,7 +100,9 @@ "uuid": "^8.3.0" }, "devDependencies": { + "@azure/dev-tool": "^1.0.0", "@azure/eslint-plugin-azure-sdk": "^3.0.0", + "@azure/identity": "^1.1.0", "@microsoft/api-extractor": "7.7.11", "@rollup/plugin-json": "^4.0.0", "@rollup/plugin-multi-entry": "^3.0.0", diff --git a/sdk/cosmosdb/cosmos/prep-samples.js b/sdk/cosmosdb/cosmos/prep-samples.js index 5999e194f8c7..7f41580c426f 100644 --- a/sdk/cosmosdb/cosmos/prep-samples.js +++ b/sdk/cosmosdb/cosmos/prep-samples.js @@ -1,4 +1,4 @@ const fs = require("fs"); // work around TS not being able to find the typings when not importing from a package reference. -fs.copyFileSync("./dist/types/latest/index.d.ts", "./dist/index.d.ts"); +fs.copyFileSync("./dist/types/latest/cosmos.d.ts", "./dist/index.d.ts"); diff --git a/sdk/cosmosdb/cosmos/review/cosmos.api.md b/sdk/cosmosdb/cosmos/review/cosmos.api.md index 7e3da45c157a..c7a2d147831e 100644 --- a/sdk/cosmosdb/cosmos/review/cosmos.api.md +++ b/sdk/cosmosdb/cosmos/review/cosmos.api.md @@ -383,11 +383,7 @@ export const Constants: { TopologyPathSegment: string; DatabaseAccountPathSegment: string; }; - PartitionKeyRange: { - MinInclusive: string; - MaxExclusive: string; - Id: string; - }; + PartitionKeyRange: PartitionKeyRangePropertiesNames; QueryRangeConstants: { MinInclusive: string; MaxExclusive: string; @@ -413,7 +409,7 @@ export class Container { getQueryPlan(query: string | SqlQuerySpec): Promise>; // (undocumented) readonly id: string; - item(id: string, partitionKeyValue?: any): Item; + item(id: string, partitionKeyValue?: PartitionKey): Item; get items(): Items; read(options?: RequestOptions): Promise; readOffer(options?: RequestOptions): Promise; @@ -527,7 +523,7 @@ export interface CreateOperationInput { // (undocumented) operationType: typeof BulkOperationType.Create; // (undocumented) - partitionKey?: string | number | null | {} | undefined; + partitionKey?: string | number | null | Record | undefined; // (undocumented) resourceBody: JSONObject; } @@ -637,7 +633,7 @@ export interface DeleteOperationInput { // (undocumented) operationType: typeof BulkOperationType.Delete; // (undocumented) - partitionKey?: string | number | null | {} | undefined; + partitionKey?: string | number | null | Record | undefined; } // @public (undocumented) @@ -671,7 +667,7 @@ export interface ErrorResponse extends Error { } // @public (undocumented) -export function extractPartitionKey(document: any, partitionKeyDefinition: PartitionKeyDefinition): PartitionKey[]; +export function extractPartitionKey(document: unknown, partitionKeyDefinition: PartitionKeyDefinition): PartitionKey[]; // @public export interface FeedOptions extends SharedOptions { @@ -853,7 +849,7 @@ export class Items { readChangeFeed(partitionKey: string | number | boolean, changeFeedOptions?: ChangeFeedOptions): ChangeFeedIterator; // @deprecated readChangeFeed(changeFeedOptions?: ChangeFeedOptions): ChangeFeedIterator; - upsert(body: any, options?: RequestOptions): Promise>; + upsert(body: unknown, options?: RequestOptions): Promise>; upsert(body: T, options?: RequestOptions): Promise>; } @@ -1004,7 +1000,7 @@ export interface PartitionedQueryExecutionInfo { } // @public (undocumented) -export type PartitionKey = PartitionKeyDefinition | string | number | {}; +export type PartitionKey = PartitionKeyDefinition | string | number | unknown; // @public (undocumented) export interface PartitionKeyDefinition { @@ -1032,6 +1028,16 @@ export interface PartitionKeyRange { throughputFraction: number; } +// @public (undocumented) +export interface PartitionKeyRangePropertiesNames { + // (undocumented) + Id: "id"; + // (undocumented) + MaxExclusive: "maxExclusive"; + // (undocumented) + MinInclusive: "minInclusive"; +} + // @public export class Permission { constructor(user: User, id: string, clientContext: ClientContext); @@ -1264,7 +1270,7 @@ export interface ReadOperationInput { // (undocumented) operationType: typeof BulkOperationType.Read; // (undocumented) - partitionKey?: string | number | null | {} | undefined; + partitionKey?: string | number | boolean | null | Record | undefined; } // @public (undocumented) @@ -1282,7 +1288,7 @@ export interface ReplaceOperationInput { // (undocumented) operationType: typeof BulkOperationType.Replace; // (undocumented) - partitionKey?: string | number | null | {} | undefined; + partitionKey?: string | number | null | Record | undefined; // (undocumented) resourceBody: JSONObject; } @@ -1514,57 +1520,56 @@ export interface SqlQuerySpec { } // @public (undocumented) -export const StatusCode: { - Ok: 200; - Created: 201; - Accepted: 202; - NoContent: 204; - NotModified: 304; - BadRequest: 400; - Unauthorized: 401; - Forbidden: 403; - NotFound: 404; - MethodNotAllowed: 405; - RequestTimeout: 408; - Conflict: 409; - Gone: 410; - PreconditionFailed: 412; - RequestEntityTooLarge: 413; - TooManyRequests: 429; - RetryWith: 449; - InternalServerError: 500; - ServiceUnavailable: 503; - OperationPaused: 1200; - OperationCancelled: number; -}; +export type StatusCode = number; // @public (undocumented) -export type StatusCode = typeof StatusCode[keyof typeof StatusCode]; +export const StatusCodes: StatusCodesType; // @public (undocumented) -export const StatusCodes: { - Ok: 200; - Created: 201; +export interface StatusCodesType { + // (undocumented) Accepted: 202; - NoContent: 204; - NotModified: 304; + // (undocumented) BadRequest: 400; - Unauthorized: 401; - Forbidden: 403; - NotFound: 404; - MethodNotAllowed: 405; - RequestTimeout: 408; + // (undocumented) Conflict: 409; + // (undocumented) + Created: 201; + // (undocumented) + Forbidden: 403; + // (undocumented) Gone: 410; + // (undocumented) + InternalServerError: 500; + // (undocumented) + MethodNotAllowed: 405; + // (undocumented) + NoContent: 204; + // (undocumented) + NotFound: 404; + // (undocumented) + NotModified: 304; + // (undocumented) + Ok: 200; + // (undocumented) + OperationCancelled: 1201; + // (undocumented) + OperationPaused: 1200; + // (undocumented) PreconditionFailed: 412; + // (undocumented) RequestEntityTooLarge: 413; - TooManyRequests: 429; + // (undocumented) + RequestTimeout: 408; + // (undocumented) RetryWith: 449; - InternalServerError: 500; + // (undocumented) ServiceUnavailable: 503; - OperationPaused: 1200; - OperationCancelled: 1201; -}; + // (undocumented) + TooManyRequests: 429; + // (undocumented) + Unauthorized: 401; +} // @public export class StoredProcedure { @@ -1572,7 +1577,7 @@ export class StoredProcedure { // (undocumented) readonly container: Container; delete(options?: RequestOptions): Promise; - execute(partitionKey: any, params?: any[], options?: RequestOptions): Promise>; + execute(partitionKey: PartitionKey, params?: any[], options?: RequestOptions): Promise>; // (undocumented) readonly id: string; read(options?: RequestOptions): Promise; @@ -1605,16 +1610,7 @@ export class StoredProcedures { } // @public (undocumented) -export const SubStatusCode: { - Unknown: 0; - CrossPartitionQueryNotServable: 1004; - PartitionKeyRangeGone: 1002; - ReadSessionNotAvailable: 1002; - WriteForbidden: number; -}; - -// @public (undocumented) -export type SubStatusCode = typeof SubStatusCode[keyof typeof SubStatusCode]; +export type SubStatusCode = number; // @public export class TimeSpan { @@ -1623,8 +1619,8 @@ export class TimeSpan { // (undocumented) static additionDoesOverflow(a: number, b: number): boolean; // (undocumented) - static compare(t1: TimeSpan, t2: TimeSpan): 0 | 1 | -1; - compareTo(value: TimeSpan): 0 | 1 | -1; + static compare(t1: TimeSpan, t2: TimeSpan): 1 | 0 | -1; + compareTo(value: TimeSpan): 1 | -1 | 0; // (undocumented) days(): number; duration(): TimeSpan; @@ -1759,7 +1755,7 @@ export interface UpsertOperationInput { // (undocumented) operationType: typeof BulkOperationType.Upsert; // (undocumented) - partitionKey?: string | number | null | {} | undefined; + partitionKey?: string | number | null | Record | undefined; // (undocumented) resourceBody: JSONObject; } diff --git a/sdk/cosmosdb/cosmos/rollup.config.js b/sdk/cosmosdb/cosmos/rollup.config.js index 867ef87a6314..5d7deee44c14 100644 --- a/sdk/cosmosdb/cosmos/rollup.config.js +++ b/sdk/cosmosdb/cosmos/rollup.config.js @@ -1,45 +1,3 @@ -import resolve from "rollup-plugin-local-resolve"; -export default [ - { - input: "dist-esm/index.js", - external: [ - "tslib", - "universal-user-agent", - "uuid", - "debug", - "node-abort-controller", - "node-fetch", - "priorityqueuejs", - "semaphore", - "crypto", - "fast-json-stable-stringify", - "jsbi" - ], - output: { - file: "dist/index.js", - format: "umd", - name: "Microsoft.Azure.Cosmos", - sourcemap: true, - globals: { - "universal-user-agent": "universalUserAgent", - "fast-json-stable-stringify": "stableStringify", - uuid: "uuid", - "node-abort-controller": "AbortController", - "node-fetch": "fetch", - crypto: "crypto", - tslib: "tslib_1", - debug: "debugLib", - priorityqueuejs: "PriorityQueue", - semaphore: "semaphore", - jsbi: "jsbi" - } - }, - plugins: [resolve()], - onwarn(warning, warn) { - if (warning.code === "CIRCULAR_DEPENDENCY") { - throw new Error(warning.message); - } - warn(warning); - } - } -]; +import { makeConfig } from "@azure/dev-tool/shared-config/rollup"; + +export default makeConfig(require("./package.json")); diff --git a/sdk/cosmosdb/cosmos/samples/.eslintrc.json b/sdk/cosmosdb/cosmos/samples/.eslintrc.json new file mode 100644 index 000000000000..94fc5dea8250 --- /dev/null +++ b/sdk/cosmosdb/cosmos/samples/.eslintrc.json @@ -0,0 +1,24 @@ +{ + "plugins": ["@azure/azure-sdk", "@typescript-eslint/tslint"], + "extends": ["plugin:@azure/azure-sdk/recommended"], + "parserOptions": { + "createDefaultProgram": true + }, + "rules": { + "@typescript-eslint/interface-name-prefix": "off", + "@typescript-eslint/no-extraneous-class": "error", + "dot-notation": "off", + "some-rule": "off", + // ChangeFeed.ts has problems with this rule + "no-irregular-whitespace": "off", + "@typescript-eslint/tslint/config": [ + "error", + { + "rules": { + "import-blacklist": [true, "assert", "util"], + "no-floating-promises": true + } + } + ] + } +} diff --git a/sdk/cosmosdb/cosmos/samples/BulkUpdateWithSproc.ts b/sdk/cosmosdb/cosmos/samples/BulkUpdateWithSproc.ts index 42cb6d30075d..cd3c64d1a470 100644 --- a/sdk/cosmosdb/cosmos/samples/BulkUpdateWithSproc.ts +++ b/sdk/cosmosdb/cosmos/samples/BulkUpdateWithSproc.ts @@ -9,7 +9,7 @@ logSampleHeader("Bulk Update Using Stored Procedures"); // Only to make TypeScript happy let getContext: any; -function body(continuation: string) { +function body(continuation: string): void { const collection = getContext().getCollection(); const response = getContext().getResponse(); const responseBody: any = { updatedDocumentIds: [] }; // Setup Initial Response @@ -28,10 +28,10 @@ function body(continuation: string) { } ); - function updateDocs(documents: any, responseBody: any) { + function updateDocs(documents: any, responseBodyParam: any): void { if (documents.length === 0) { // If no documents are left to update, we are done - response.setBody(responseBody); + response.setBody(responseBodyParam); } else { // Grab the next document to update const document = documents.pop(); @@ -39,9 +39,9 @@ function body(continuation: string) { collection.replaceDocument(document._self, document, {}, function(err: any) { if (err) throw err; // If we have successfully updated the document, include it in the returned document ids - responseBody.updatedDocumentIds.push(document.id); - // Call update with remaning documents - updateDocs(documents, responseBody); + responseBodyParam.updatedDocumentIds.push(document.id); + // Call update with remaining documents + updateDocs(documents, responseBodyParam); }); } } @@ -50,7 +50,7 @@ function body(continuation: string) { // Establish a new instance of the CosmosClient to be used throughout this demo const client = new CosmosClient({ endpoint, key }); -async function run() { +async function run(): Promise { // ensuring a database & container exists for us to work with logStep("Create database '" + databaseId + "' and container '" + containerId + "'"); const { database } = await client.databases.createIfNotExists({ id: databaseId }); @@ -72,7 +72,7 @@ async function run() { logStep("Execute stored procedure and follow continuation tokens"); let continuation: string = undefined; let totalUpdatedDocuments = 0; - while (true) { + for (;;) { const response = await storedProcedure.execute(undefined, [continuation]); const result: any = response.resource; totalUpdatedDocuments = totalUpdatedDocuments + result.updatedDocumentIds.length; diff --git a/sdk/cosmosdb/cosmos/samples/ChangeFeed.ts b/sdk/cosmosdb/cosmos/samples/ChangeFeed.ts index 4a19a7a17185..749971e3a1fb 100644 --- a/sdk/cosmosdb/cosmos/samples/ChangeFeed.ts +++ b/sdk/cosmosdb/cosmos/samples/ChangeFeed.ts @@ -1,5 +1,6 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. + import { finish, handleError, logSampleHeader } from "./Shared/handleError"; import { CosmosClient } from "../dist"; import { database as databaseId, container as containerId, endpoint, key } from "./Shared/config"; @@ -11,7 +12,7 @@ const client = new CosmosClient({ endpoint, key }); // We'll use the same pk value for all these samples const pk = "0"; -function doesMatch(actual: any[], expected: any[]) { +function doesMatch(actual: any[], expected: any[]): string { for (let i = 0; i < actual.length; i++) { if (actual[i] !== expected[i]) { return "❌"; @@ -20,14 +21,14 @@ function doesMatch(actual: any[], expected: any[]) { return "✅"; } -function logResult(scenario: string, actual: any[], expected: any[]) { +function logResult(scenario: string, actual: any[], expected: any[]): void { const status = doesMatch(actual, expected); console.log( ` ${status} ${scenario} - expected: [${expected.join(", ")}] - actual: [${actual.join(", ")}]` ); } -async function run() { +async function run(): Promise { const { database } = await client.databases.createIfNotExists({ id: databaseId }); const { container } = await database.containers.createIfNotExists({ id: containerId, @@ -142,7 +143,11 @@ async function run() { ); const { result: fromNowResults2 } = await fromNowIterator.fetchNext(); - logResult("after insert, from now scenario", [4], fromNowResults2.map((v) => parseInt(v.id))); + logResult( + "after insert, from now scenario", + [4], + fromNowResults2.map((v) => parseInt(v.id)) + ); } catch (err) { handleError(err); } finally { diff --git a/sdk/cosmosdb/cosmos/samples/ContainerManagement.ts b/sdk/cosmosdb/cosmos/samples/ContainerManagement.ts index 8cc7f00f3181..248c0bf9e479 100644 --- a/sdk/cosmosdb/cosmos/samples/ContainerManagement.ts +++ b/sdk/cosmosdb/cosmos/samples/ContainerManagement.ts @@ -10,7 +10,7 @@ logSampleHeader("Container Management"); const client = new CosmosClient({ endpoint, key }); // ensuring a database exists for us to work with -async function run() { +async function run(): Promise { const { database } = await client.databases.createIfNotExists({ id: databaseId }); logStep(`Create container with id : ${containerId}`); diff --git a/sdk/cosmosdb/cosmos/samples/DatabaseManagement.ts b/sdk/cosmosdb/cosmos/samples/DatabaseManagement.ts index d2c3d3b7e0ed..3fc82d673677 100644 --- a/sdk/cosmosdb/cosmos/samples/DatabaseManagement.ts +++ b/sdk/cosmosdb/cosmos/samples/DatabaseManagement.ts @@ -3,6 +3,7 @@ import { handleError, logStep, logSampleHeader, finish } from "./Shared/handleError"; import { CosmosClient } from "../dist"; import { endpoint, key, database as databaseId } from "./Shared/config"; +// eslint-disable-next-line @typescript-eslint/tslint/config import assert from "assert"; logSampleHeader("Database Management"); @@ -10,7 +11,7 @@ logSampleHeader("Database Management"); // Establish a new instance of the CosmosClient to be used throughout this demo const client = new CosmosClient({ endpoint, key }); -async function run() { +async function run(): Promise { logStep("Create database, if it doesn't already exist"); await client.databases.createIfNotExists({ id: databaseId }); console.log("Database with id " + databaseId + " created."); diff --git a/sdk/cosmosdb/cosmos/samples/IndexManagement.ts b/sdk/cosmosdb/cosmos/samples/IndexManagement.ts index b44511a1c407..1cc77e4154e6 100644 --- a/sdk/cosmosdb/cosmos/samples/IndexManagement.ts +++ b/sdk/cosmosdb/cosmos/samples/IndexManagement.ts @@ -9,12 +9,14 @@ logSampleHeader("Index Management"); // Establish a new instance of the CosmosClient to be used throughout this demo const client = new CosmosClient({ endpoint, key }); -async function run() { +async function run(): Promise { const { database } = await client.databases.createIfNotExists({ id: databaseId }); // We're using the default indexing policy because by default indexingMode == consistent & automatic == true // which means that by default all items added to a container are indexed as the item is written - const { container, resource: containerDef } = await database.containers.createIfNotExists({ id: containerId }); + const { container, resource: containerDef } = await database.containers.createIfNotExists({ + id: containerId + }); logStep("Manually exclude an item from being indexed"); console.log("create container with default index policy"); @@ -82,8 +84,8 @@ async function run() { if (results2.length === 0) { throw new Error("There were meant to be results"); } else { - const itemDef = results2[0]; - console.log("Item with id '" + itemDef.id + "' found"); + const fetchedItemDef = results2[0]; + console.log("Item with id '" + fetchedItemDef.id + "' found"); } logStep("Create a range index on string path"); diff --git a/sdk/cosmosdb/cosmos/samples/ItemManagement.ts b/sdk/cosmosdb/cosmos/samples/ItemManagement.ts index 2eec37d261b7..de87dc6b22f0 100644 --- a/sdk/cosmosdb/cosmos/samples/ItemManagement.ts +++ b/sdk/cosmosdb/cosmos/samples/ItemManagement.ts @@ -12,7 +12,7 @@ const itemDefs = JSON.parse(readFileSync("./Shared/Data/Families.json", "utf8")) // Establish a new instance of the CosmosClient to be used throughout this demo const client = new CosmosClient({ endpoint, key }); -async function run() { +async function run(): Promise { // ensuring a database & container exists for us to work with const { database } = await client.databases.createIfNotExists({ id: databaseId }); const { container } = await database.containers.createIfNotExists({ id: containerId }); @@ -38,7 +38,7 @@ async function run() { const { resource: item2, headers } = await item.read({ accessCondition: { type: "IfNoneMatch", condition: readDoc._etag } }); - if (!item2 && headers["content-length"] == 0) { + if (!item2 && headers["content-length"] === 0) { console.log( "As expected, no item returned. This is because the etag sent matched the etag on the server. i.e. you have the latest version of the item already" ); @@ -54,7 +54,9 @@ async function run() { if (!item3 && headers3["content-length"] === 0) { throw "Expected item this time. Something is wrong!"; } else { - console.log("This time the read request returned the item because the etag values did not match"); + console.log( + "This time the read request returned the item because the etag values did not match" + ); } const querySpec = { @@ -70,7 +72,7 @@ async function run() { logStep("Query items in container '" + container.id + "'"); const { resources: results } = await container.items.query(querySpec).fetchAll(); - if (results.length == 0) { + if (results.length === 0) { throw "No items found matching"; } else if (results.length > 1) { throw "More than 1 item found matching"; @@ -95,7 +97,9 @@ async function run() { const { resource: updatedPerson } = await container.items.upsert(person); console.log("The '" + person.id + "' family has lastName '" + updatedPerson.lastName + "'"); - console.log("The '" + person.id + "' family has " + updatedPerson.children.length + " children '"); + console.log( + "The '" + person.id + "' family has " + updatedPerson.children.length + " children '" + ); logStep("Trying to replace item when item has changed in the database"); // The replace item above will work even if there's a new version of item on the server from what you originally read @@ -113,7 +117,7 @@ async function run() { await item.replace(person, { accessCondition: { type: "IfMatch", condition: person._etag } }); throw new Error("This should have failed!"); } catch (err) { - if (err.code == 412) { + if (err.code === 412) { console.log("As expected, the replace item failed with a pre-condition failure"); } else { throw err; @@ -133,8 +137,9 @@ async function run() { const { resource: upsertedPerson2 } = await container.items.upsert(upsertSource); console.log(`Upserted ${upsertedPerson2.id} to id ${upsertedPerson2.id}.`); - if (upsertedPerson1.id === upsertedPerson2.id) - {throw new Error("These two upserted records should have different resource IDs.");} + if (upsertedPerson1.id === upsertedPerson2.id) { + throw new Error("These two upserted records should have different resource IDs."); + } logStep("Delete item '" + item.id + "'"); await item.delete(); diff --git a/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/ConflictWorker.ts b/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/ConflictWorker.ts index b1e081da03d0..fef4810b6f6f 100644 --- a/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/ConflictWorker.ts +++ b/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/ConflictWorker.ts @@ -2,7 +2,15 @@ // Licensed under the MIT license. // tslint:disable:no-console import { v4 as guid } from "uuid"; -import { CosmosClient, Item, ItemDefinition, Items, OperationType, Resource, StatusCodes } from "../../dist"; +import { + CosmosClient, + Item, + ItemDefinition, + Items, + OperationType, + Resource, + StatusCodes +} from "../../dist"; import logger from "./logger"; import lwwSprocDef from "./lwwSprocDef"; @@ -16,7 +24,7 @@ export class ConflictWorker { private readonly udpContainerName: string ) {} - public addClient(region: string, client: CosmosClient) { + public addClient(region: string, client: CosmosClient): void { this.clients.set(region, client); } @@ -24,14 +32,14 @@ export class ConflictWorker { const createClient = this.clients.values().next().value; const { database } = await createClient.databases.createIfNotExists({ id: this.databaseName }); - const { container: basicContainer } = await database.containers.createIfNotExists({ id: this.basicContainerName }); - const { container: manualContainer } = await database.containers.createIfNotExists({ + await database.containers.createIfNotExists({ id: this.basicContainerName }); + await database.containers.createIfNotExists({ id: this.manualContainerName, conflictResolutionPolicy: { mode: "Custom" } }); - const { container: lwwContainer } = await database.containers.createIfNotExists({ + await database.containers.createIfNotExists({ id: this.lwwContainerName, conflictResolutionPolicy: { mode: "LastWriterWins", @@ -47,10 +55,10 @@ export class ConflictWorker { }); // See ./lwwSprocDef for the stored procedure definition include the logic - const { sproc: lwwSproc } = await udpContainer.scripts.storedProcedures.create(lwwSprocDef); + await udpContainer.scripts.storedProcedures.create(lwwSprocDef); } - public async RunManualConflict() { + public async RunManualConflict(): Promise { console.log("Insert Conflict"); await this.RunInsertConflictonManual(); @@ -61,7 +69,7 @@ export class ConflictWorker { await this.RunDeleteConflictOnManual(); } - public async RunLWWConflict() { + public async RunLWWConflict(): Promise { console.log("Insert Conflict"); await this.RunInsertConflictOnLWW(); @@ -72,7 +80,7 @@ export class ConflictWorker { await this.RunDeleteConflictOnLWW(); } - public async RunUDP() { + public async RunUDP(): Promise { console.log("Insert Conflict"); await this.RunInsertConflictOnUdp(); @@ -83,7 +91,7 @@ export class ConflictWorker { await this.RunDeleteConflictsOnUdp(); } - private async RunInsertConflictonManual() { + private async RunInsertConflictonManual(): Promise { do { let p = logger( `Performing conflicting insert across ${this.clients.size} regions on ${this.manualContainerName}` @@ -102,7 +110,10 @@ export class ConflictWorker { const items = await Promise.all(insertTask); p.succeed(); - const numberOfConflicts = items.reduce((prev, curr) => (curr !== null ? ++prev : prev), 0); + const numberOfConflicts = items.reduce( + (prev, curr) => (curr !== null ? ++prev : prev), + 0 + ); if (numberOfConflicts > 1) { p = logger(`Caused ${numberOfConflicts}, verifying conflict resolution`).succeed(); @@ -120,16 +131,18 @@ export class ConflictWorker { p.fail(); throw err; } - } while (true); + } while (true); // eslint-disable-line no-constant-condition } - private async RunUpdateConflictOnManual() { + private async RunUpdateConflictOnManual(): Promise { let retryCount = 5; do { const itemBase = { id: guid() }; const [initialRegionName, initialClient] = this.clients.entries().next().value; - const container = initialClient.database(this.databaseName).container(this.manualContainerName); + const container = initialClient + .database(this.databaseName) + .container(this.manualContainerName); const item = { regionId: 0, regionEndpoint: initialRegionName, ...itemBase }; // TODO: ReadEndpoint? const { resource: newItemDef } = await container.items.create(item); @@ -160,9 +173,14 @@ export class ConflictWorker { } const updatedItems = await Promise.all(updates); - const numberOfConflicts = updatedItems.reduce((p: number, c: ItemDefinition) => (c !== null ? ++p : p), -1); + const numberOfConflicts = updatedItems.reduce( + (p: number, c: ItemDefinition) => (c !== null ? ++p : p), + -1 + ); if (numberOfConflicts > 0) { - console.log(`2) Caused ${numberOfConflicts} update conflicts, verifying conflict resolution`); + console.log( + `2) Caused ${numberOfConflicts} update conflicts, verifying conflict resolution` + ); for (const updatedItem of updatedItems) { if (updatedItem) { @@ -177,12 +195,14 @@ export class ConflictWorker { console.error("Could not enduce an update conflict for manual conflict resolution"); } - private async RunDeleteConflictOnManual() { + private async RunDeleteConflictOnManual(): Promise { do { const itemBase = { id: guid() }; const [initialRegionName, initialClient] = this.clients.entries().next().value; - const container = initialClient.database(this.databaseName).container(this.manualContainerName); + const container = initialClient + .database(this.databaseName) + .container(this.manualContainerName); const item = { regionId: 0, regionEndpoint: initialRegionName, ...itemBase }; // TODO: ReadEndpoint? const { resource: newItemDef } = await container.items.create(item); @@ -195,7 +215,13 @@ export class ConflictWorker { const deletes: Array> = []; let index = 0; for (const [regionName, client] of this.clients.entries()) { - const newDef = { regionId: index++, regionName, ...itemBase, _etag: newItemDef._etag, _rid: newItemDef._rid }; + const newDef = { + regionId: index++, + regionName, + ...itemBase, + _etag: newItemDef._etag, + _rid: newItemDef._rid + }; deletes.push( this.tryDeleteItem( client @@ -208,9 +234,14 @@ export class ConflictWorker { } const deletedItems = await Promise.all(deletes); - const numberOfConflicts = deletedItems.reduce((p: number, c: ItemDefinition) => (c !== null ? ++p : p), -1); + const numberOfConflicts = deletedItems.reduce( + (p: number, c: ItemDefinition) => (c !== null ? ++p : p), + -1 + ); if (numberOfConflicts > 1) { - console.log(`2) Caused ${numberOfConflicts} delete conflicts, verifying conflict resolution`); + console.log( + `2) Caused ${numberOfConflicts} delete conflicts, verifying conflict resolution` + ); await this.validateLWW(deletedItems, true); // LWW deletes and manual deletes are handled the same @@ -218,10 +249,10 @@ export class ConflictWorker { } else { console.warn("Retrying update/delete to induce conflicts"); } - } while (true); + } while (true); // eslint-disable-line no-constant-condition } - private async validateAllManualConflict(item: ItemDefinition) { + private async validateAllManualConflict(item: ItemDefinition): Promise { let conflictExists = false; for (const [conflictRegion, client] of this.clients.entries()) { conflictExists = await this.validateManualConflict(conflictRegion, client, item); @@ -232,8 +263,12 @@ export class ConflictWorker { } } - private async validateManualConflict(clientRegion: string, client: CosmosClient, item: ItemDefinition) { - while (true) { + private async validateManualConflict( + clientRegion: string, + client: CosmosClient, + item: ItemDefinition + ): Promise { + for (;;) { const container = client.database(this.databaseName).container(this.manualContainerName); const { resources: conflicts } = await container.conflicts.readAll().fetchAll(); @@ -250,8 +285,10 @@ export class ConflictWorker { return true; } else { try { - const winner = client.database(this.databaseName).container(this.manualContainerName); - console.log(`Document from region ${item.regionId} won the conflict @ ${clientRegion}`); + client.database(this.databaseName).container(this.manualContainerName); + console.log( + `Document from region ${item.regionId} won the conflict @ ${clientRegion}` + ); return false; } catch (err) { if (err.code && err.code === StatusCodes.NotFound) { @@ -272,9 +309,11 @@ export class ConflictWorker { } } - private async RunInsertConflictOnLWW() { + private async RunInsertConflictOnLWW(): Promise { do { - console.log(`1) Performing conflicting insert across ${this.clients.size} regions on ${this.lwwContainerName}`); + console.log( + `1) Performing conflicting insert across ${this.clients.size} regions on ${this.lwwContainerName}` + ); const inserts: Array> = []; const itemBase = { id: guid() }; @@ -286,7 +325,7 @@ export class ConflictWorker { inserts.push(this.tryInsertItem(container.items, newDef)); } - const items = (await Promise.all(inserts)).filter(v => v !== null); + const items = (await Promise.all(inserts)).filter((v) => v !== null); if (items.length > 1) { console.log(`2) Caused ${items.length} insert conflicts, verifying conflict resolution`); @@ -296,10 +335,10 @@ export class ConflictWorker { } else { console.warn("Retrying insert to induce conflicts"); } - } while (true); + } while (true); // eslint-disable-line no-constant-condition } - private async RunUpdateConflictOnLWW() { + private async RunUpdateConflictOnLWW(): Promise { let retry = 5; do { const itemBase = { id: guid() }; @@ -311,7 +350,9 @@ export class ConflictWorker { await this.sleep(1000); // 1 second for the write to sync - console.log(`1) Performing conflicting update across ${this.clients.size} regions on ${this.lwwContainerName}`); + console.log( + `1) Performing conflicting update across ${this.clients.size} regions on ${this.lwwContainerName}` + ); const updates: Array> = []; let index = 0; @@ -328,7 +369,7 @@ export class ConflictWorker { ); } - const items = (await Promise.all(updates)).filter(v => v !== null); + const items = (await Promise.all(updates)).filter((v) => v !== null); if (items.length > 1) { console.log(`2) Caused ${items.length} update conflicts, verifying conflict resolution`); @@ -342,7 +383,7 @@ export class ConflictWorker { console.error("Could not induce update conflict on LWW"); } - private async RunDeleteConflictOnLWW() { + private async RunDeleteConflictOnLWW(): Promise { do { const itemBase = { id: guid() }; @@ -353,7 +394,9 @@ export class ConflictWorker { await this.sleep(1000); // 1 second for the write to sync - console.log(`1) Performing conflicting delete across ${this.clients.size} regions on ${this.lwwContainerName}`); + console.log( + `1) Performing conflicting delete across ${this.clients.size} regions on ${this.lwwContainerName}` + ); const deletes: Array> = []; let index = 0; @@ -382,7 +425,7 @@ export class ConflictWorker { } } - const items = (await Promise.all(deletes)).filter(v => v !== null); + const items = (await Promise.all(deletes)).filter((v) => v !== null); if (items.length > 2) { console.log(`2) Caused ${items.length} delete conflicts, verifying conflict resolution`); @@ -391,10 +434,13 @@ export class ConflictWorker { } else { console.warn("Retrying update/delete to induce conflicts"); } - } while (true); + } while (true); // eslint-disable-line no-constant-condition } - private async validateLWW(items: ItemDefinition[], hasDeleteConflict: boolean = false) { + private async validateLWW( + items: ItemDefinition[], + hasDeleteConflict: boolean = false + ): Promise { for (const [regionName, client] of this.clients.entries()) { await this.validateLWWPerClient(regionName, client, items, hasDeleteConflict); } @@ -405,7 +451,7 @@ export class ConflictWorker { client: CosmosClient, items: ItemDefinition[], hasDeleteConflict: boolean - ) { + ): Promise { const container = client.database(this.databaseName).container(this.lwwContainerName); const { resources: conflicts } = await container.conflicts.readAll().fetchAll(); @@ -427,14 +473,14 @@ export class ConflictWorker { } console.error(`Delete conflict for item ${items[0].id} didn't win @ ${regionName}`); await this.sleep(500); - } while (true); + } while (true); // eslint-disable-line no-constant-condition } const winner = items.reduce((p, c) => (p.regionId <= c.regionId ? c : p), items[0]); console.log(`Document from region ${winner.regionId} should be the winner`); - while (true) { + for (;;) { try { const { resource: currentItem } = await container.item(winner.id, undefined).read(); @@ -453,9 +499,11 @@ export class ConflictWorker { } } - public async RunInsertConflictOnUdp() { + public async RunInsertConflictOnUdp(): Promise { do { - console.log(`1) Performing conflicting insert across ${this.clients.size} regions on ${this.udpContainerName}`); + console.log( + `1) Performing conflicting insert across ${this.clients.size} regions on ${this.udpContainerName}` + ); const inserts: Array> = []; const itemBase = { id: guid() }; @@ -467,7 +515,7 @@ export class ConflictWorker { inserts.push(this.tryInsertItem(container.items, newDef)); } - const items = (await Promise.all(inserts)).filter(v => v !== null); + const items = (await Promise.all(inserts)).filter((v) => v !== null); if (items.length > 1) { console.log(`2) Caused ${items.length} insert conflicts, verifying conflict resolution`); @@ -477,10 +525,10 @@ export class ConflictWorker { } else { console.warn("Retrying insert to induce conflicts"); } - } while (true); + } while (true); // eslint-disable-line no-constant-condition } - public async RunUpdateConflictOnUdp() { + public async RunUpdateConflictOnUdp(): Promise { do { const itemBase = { id: guid() }; @@ -491,7 +539,9 @@ export class ConflictWorker { await this.sleep(1000); // 1 second for the write to sync - console.log(`1) Performing conflicting update across ${this.clients.size} regions on ${this.udpContainerName}`); + console.log( + `1) Performing conflicting update across ${this.clients.size} regions on ${this.udpContainerName}` + ); const updates: Array> = []; let index = 0; @@ -508,7 +558,7 @@ export class ConflictWorker { ); } - const items = (await Promise.all(updates)).filter(v => v !== null); + const items = (await Promise.all(updates)).filter((v) => v !== null); if (items.length > 1) { console.log(`2) Caused ${items.length} update conflicts, verifying conflict resolution`); @@ -518,10 +568,10 @@ export class ConflictWorker { } else { console.warn("Retrying update to induce conflicts"); } - } while (true); + } while (true); // eslint-disable-line no-constant-condition } - public async RunDeleteConflictsOnUdp() { + public async RunDeleteConflictsOnUdp(): Promise { do { const itemBase = { id: guid() }; @@ -532,7 +582,9 @@ export class ConflictWorker { await this.sleep(1000); // 1 second for the write to sync - console.log(`1) Performing conflicting delete across ${this.clients.size} regions on ${this.udpContainerName}`); + console.log( + `1) Performing conflicting delete across ${this.clients.size} regions on ${this.udpContainerName}` + ); const deletes: Array> = []; let index = 0; @@ -561,7 +613,7 @@ export class ConflictWorker { } } - const items = (await Promise.all(deletes)).filter(v => v !== null); + const items = (await Promise.all(deletes)).filter((v) => v !== null); if (items.length > 2) { console.log(`2) Caused ${items.length} delete conflicts, verifying conflict resolution`); @@ -570,10 +622,13 @@ export class ConflictWorker { } else { console.warn("Retrying update/delete to induce conflicts"); } - } while (true); + } while (true); // eslint-disable-line no-constant-condition } - private async validateUDP(items: ItemDefinition[], hasDeleteConflict: boolean = false) { + private async validateUDP( + items: ItemDefinition[], + hasDeleteConflict: boolean = false + ): Promise { for (const [regionName, client] of this.clients.entries()) { await this.validateUDPPerClient(regionName, client, items, hasDeleteConflict); } @@ -584,7 +639,7 @@ export class ConflictWorker { client: CosmosClient, items: ItemDefinition, hasDeleteConflict: boolean - ) { + ): Promise { const container = client.database(this.databaseName).container(this.udpContainerName); const { resources: conflicts } = await container.conflicts.readAll().fetchAll(); @@ -597,7 +652,7 @@ export class ConflictWorker { if (hasDeleteConflict) { do { try { - const { resource: shouldNotExist } = await container.item(items[0].id, undefined).read(); + await container.item(items[0].id, undefined).read(); } catch (err) { if (err.code === StatusCodes.NotFound) { console.log(`Delete conflict won @ ${regionName}`); @@ -606,14 +661,17 @@ export class ConflictWorker { } console.error(`Delete conflict for item ${items[0].id} didn't win @ ${regionName}`); await this.sleep(500); - } while (true); + } while (true); // eslint-disable-line no-constant-condition } - const winner = items.reduce((p: ItemDefinition, c: ItemDefinition) => (p.regionId <= c.regionId ? c : p), items[0]); + const winner = items.reduce( + (p: ItemDefinition, c: ItemDefinition) => (p.regionId <= c.regionId ? c : p), + items[0] + ); console.log(`Document from region ${winner.regionId} should be the winner`); - while (true) { + for (;;) { try { const { resource: currentItem } = await container.item(winner.id, undefined).read(); @@ -644,18 +702,25 @@ export class ConflictWorker { } } - private async tryUpdateItem(item: Item, newDef: ItemDefinition): Promise { + private async tryUpdateItem( + item: Item, + newDef: ItemDefinition + ): Promise { const time = Date.now(); try { - return (await item.replace(newDef, { - accessCondition: { - type: "IfMatch", - condition: newDef._etag - } - })).resource; + return ( + await item.replace(newDef, { + accessCondition: { + type: "IfMatch", + condition: newDef._etag + } + }) + ).resource; } catch (err) { if (err.code === StatusCodes.PreconditionFailed || err.code === StatusCodes.NotFound) { - console.log(`${await item.container.database.client.getWriteEndpoint()} hit ${err.code} at ${time}`); + console.log( + `${await item.container.database.client.getWriteEndpoint()} hit ${err.code} at ${time}` + ); return null; // Lost synchronously or not document yet. No conflict is induced. } else { console.log("tryUpdateItem hit unexpected error"); @@ -666,7 +731,7 @@ export class ConflictWorker { private async tryDeleteItem(item: Item, newDef: ItemDefinition): Promise { try { - const { resource: deletedItem } = await item.delete({ + await item.delete({ accessCondition: { type: "IfMatch", condition: newDef._etag @@ -682,7 +747,7 @@ export class ConflictWorker { } } - private async DeleteConflict(item: ItemDefinition) { + private async DeleteConflict(item: ItemDefinition): Promise { const client = this.clients.values().next().value; const container = client.database(this.databaseName).container(this.manualContainerName); const conflicts = await container.conflicts.readAll().fetchAll(); @@ -690,8 +755,14 @@ export class ConflictWorker { for (const conflict of conflicts.resources) { if (conflict.operationType !== OperationType.Delete) { const content = JSON.parse(conflict.content); - if (content._rid === item._rid && content._etag === item._etag && content.regionId === item.regionId) { - console.log(`Deleting manual conflict ${conflict.resourceId} from region ${item.regionId}`); + if ( + content._rid === item._rid && + content._etag === item._etag && + content.regionId === item.regionId + ) { + console.log( + `Deleting manual conflict ${conflict.resourceId} from region ${item.regionId}` + ); await container.conflict(conflict.id).delete(); } } else if (conflict.resourceId === item._rid) { @@ -702,9 +773,9 @@ export class ConflictWorker { } private sleep(timeinMS: number): Promise { - return new Promise((res, rej) => { + return new Promise((resolve) => { setTimeout(() => { - res(); + resolve(); }, timeinMS); }); } diff --git a/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/MultiRegionWriteScenario.ts b/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/MultiRegionWriteScenario.ts index 9be79127a2b3..2818384e5be7 100644 --- a/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/MultiRegionWriteScenario.ts +++ b/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/MultiRegionWriteScenario.ts @@ -26,28 +26,33 @@ export class MultiRegionWriteScenario { }); this.conflictWorker.addClient(region, client); this.basicWorkers.push( - new Worker(region, client.database(config.databaseName).container(config.basicCollectionName)) + new Worker( + region, + client.database(config.databaseName).container(config.basicCollectionName) + ) ); } } - public async init() { + public async init(): Promise { await this.conflictWorker.init(); console.log("Initialized containers"); } - public async runBasic() { + public async runBasic(): Promise { console.log("################################################"); console.log("Basic Active-Active"); console.log("################################################"); console.log("1) Starting insert loops across multiple regions"); - await Promise.all(this.basicWorkers.map(worker => worker.RunLoop(100))); + await Promise.all(this.basicWorkers.map((worker) => worker.RunLoop(100))); console.log("2) Reading from every region..."); - await Promise.all(this.basicWorkers.map(worker => worker.ReadAll(100 * this.basicWorkers.length))); + await Promise.all( + this.basicWorkers.map((worker) => worker.ReadAll(100 * this.basicWorkers.length)) + ); console.log("3) Deleting all the documents"); @@ -56,7 +61,7 @@ export class MultiRegionWriteScenario { console.log("################################################"); } - public async runManualConflict() { + public async runManualConflict(): Promise { console.log("################################################"); console.log("Manual Conflict Resolution"); console.log("################################################"); @@ -65,7 +70,7 @@ export class MultiRegionWriteScenario { console.log("################################################"); } - public async runLWW() { + public async runLWW(): Promise { console.log("################################################"); console.log("LWW Conflict Resolution"); console.log("################################################"); @@ -74,7 +79,7 @@ export class MultiRegionWriteScenario { console.log("################################################"); } - public async runUDP() { + public async runUDP(): Promise { console.log("################################################"); console.log("UDP Conflict Resolution"); console.log("################################################"); diff --git a/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/Worker.ts b/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/Worker.ts index 134ef92c780a..5a4daffedb5a 100644 --- a/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/Worker.ts +++ b/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/Worker.ts @@ -7,7 +7,7 @@ import { Container } from "../../dist"; export class Worker { constructor(private readonly regionName: string, private readonly container: Container) {} - public async RunLoop(itemsToInsert: number) { + public async RunLoop(itemsToInsert: number): Promise { let iterationCount = 0; let latency: number[] = []; @@ -25,8 +25,8 @@ export class Worker { ); } - public async ReadAll(expectedNumberOfItems: number) { - while (true) { + public async ReadAll(expectedNumberOfItems: number): Promise { + for (;;) { const { resources: items } = await this.container.items.readAll().fetchAll(); if (items.length < expectedNumberOfItems) { console.log( @@ -41,7 +41,7 @@ export class Worker { } } - public async DeleteAll() { + public async DeleteAll(): Promise { const { resources: items } = await this.container.items.readAll().fetchAll(); for (const item of items) { await this.container.item(item.id, undefined).delete(); diff --git a/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/app.ts b/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/app.ts index 1ace9ca7e128..806d0b04e409 100644 --- a/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/app.ts +++ b/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/app.ts @@ -3,7 +3,7 @@ import { MultiRegionWriteScenario } from "./MultiRegionWriteScenario"; // tslint:disable:no-console -async function run() { +async function run(): Promise { const scenarios = new MultiRegionWriteScenario(); await scenarios.init(); @@ -13,12 +13,12 @@ async function run() { await scenarios.runUDP(); } -run() - .catch(err => { +run() // eslint-disable-line promise/catch-or-return + .catch((err) => { console.error(err); process.exit(1); }) .then(() => { console.log("Complete!"); - process.exit(0); + return; }); diff --git a/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/logger.ts b/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/logger.ts index b0eee12bc144..4aa636d241f8 100644 --- a/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/logger.ts +++ b/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/logger.ts @@ -2,7 +2,7 @@ // Licensed under the MIT license. import * as Ora from "ora"; -export default (text: string) => { +export default (text: string): any => { return new Ora({ spinner: "clock", text diff --git a/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/types.d.ts b/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/types.d.ts index ee5a4a0d7cbd..c5bacebe0cf9 100644 --- a/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/types.d.ts +++ b/sdk/cosmosdb/cosmos/samples/MultiRegionWrite/types.d.ts @@ -1,4 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. + +/* eslint-disable @azure/azure-sdk/ts-no-namespaces */ + declare module "ora"; declare module "uuid"; diff --git a/sdk/cosmosdb/cosmos/samples/QueryThroughput/alterQueryThroughput.ts b/sdk/cosmosdb/cosmos/samples/QueryThroughput/alterQueryThroughput.ts index ba910d9dc0b4..14c438316c2c 100644 --- a/sdk/cosmosdb/cosmos/samples/QueryThroughput/alterQueryThroughput.ts +++ b/sdk/cosmosdb/cosmos/samples/QueryThroughput/alterQueryThroughput.ts @@ -17,7 +17,7 @@ logSampleHeader("Alter Query Throughput"); const client = new CosmosClient({ endpoint, key }); // ensuring a database exists for us to work with -async function run() { +async function run(): Promise { const { database } = await client.databases.createIfNotExists({ id: databaseId }); logStep(`Create container with id : ${containerId}`); @@ -56,7 +56,6 @@ async function updateOfferForCollection( oldOfferDefinition: OfferDefinition ): Promise { if (!oldOfferDefinition || !oldOfferDefinition.content) throw "found invalid offer"; - const oldRups = oldOfferDefinition.content.offerThroughput; const newOfferDefinition: OfferDefinition = { ...oldOfferDefinition, content: { @@ -82,6 +81,8 @@ async function updateOfferForCollection( }) ); + const flat = (nestedArrays: T[][]): T[] => [].concat(...nestedArrays); + const containers: (ContainerDefinition & Resource)[] = flat( containerResponses.map( (response: FeedResponse) => response.resources @@ -90,8 +91,9 @@ async function updateOfferForCollection( logStep("Finding container to offerDefinition"); const container = containers.find( - (container: ContainerDefinition & Resource) => - container._rid === oldOfferDefinition.offerResourceId && container.id === collectionName + (containerParam: ContainerDefinition & Resource) => + containerParam._rid === oldOfferDefinition.offerResourceId && + containerParam.id === collectionName ); if (container) { @@ -109,5 +111,3 @@ async function asyncForEach( await callback(array[index]); } } - -const flat = (nestedArrays: T[][]): T[] => [].concat(...nestedArrays); diff --git a/sdk/cosmosdb/cosmos/samples/ServerSideScripts/index.ts b/sdk/cosmosdb/cosmos/samples/ServerSideScripts/index.ts index 53b9a1ec99e0..c92e651150fd 100644 --- a/sdk/cosmosdb/cosmos/samples/ServerSideScripts/index.ts +++ b/sdk/cosmosdb/cosmos/samples/ServerSideScripts/index.ts @@ -10,7 +10,7 @@ logSampleHeader("Server Side Scripts"); const client = new CosmosClient({ endpoint, key }); // Path to stored procedure definition -const sprocDefinition = require("./upsert"); +const sprocDefinition = require("./upsert"); // eslint-disable-line @typescript-eslint/no-require-imports // Execute the stored procedure with the following parameters. const sprocParams = [ @@ -20,14 +20,14 @@ const sprocParams = [ } ]; -async function run() { +async function run(): Promise { const { database } = await client.databases.create({ id: databaseId }); const { container } = await database.containers.create({ id: containerId }); logStep("Creating the sproc: '" + sprocDefinition.id + "'"); // Query for the stored procedure. - const { sproc, resource: sprocDef } = await container.scripts.storedProcedures.create(sprocDefinition); + const { sproc } = await container.scripts.storedProcedures.create(sprocDefinition); logStep("Executing the sproc: '" + sproc.id + "'"); console.log("Sproc parameters: " + JSON.stringify(sprocParams)); diff --git a/sdk/cosmosdb/cosmos/samples/Shared/handleError.ts b/sdk/cosmosdb/cosmos/samples/Shared/handleError.ts index b5637db68538..726efa26a764 100644 --- a/sdk/cosmosdb/cosmos/samples/Shared/handleError.ts +++ b/sdk/cosmosdb/cosmos/samples/Shared/handleError.ts @@ -5,30 +5,32 @@ import { CosmosClient } from "../../dist"; const client = new CosmosClient({ endpoint, key }); -export async function handleError(error: any) { +export async function handleError(error: { code: string }): Promise { console.log("\nAn error with code '" + error.code + "' has occurred:"); console.log(error); await finish(); process.exitCode = 1; } -export async function finish() { +export async function finish(): Promise { try { await client.database(database).delete(); console.log("\nEnd of demo."); } catch (err) { - console.log(`Database: "${database}" might not have deleted properly. You might need to delete it manually.`); + console.log( + `Database: "${database}" might not have deleted properly. You might need to delete it manually.` + ); process.exitCode = 1; } } let currentStep = 0; -export function logStep(message: string) { +export function logStep(message: string): void { currentStep++; console.log(`\n${currentStep}: ${message}`); } -export function logSampleHeader(sampleName: string) { +export function logSampleHeader(sampleName: string): void { console.log("Azure Cosmos DB Node.js Samples"); console.log("================================"); console.log(sampleName); diff --git a/sdk/cosmosdb/cosmos/samples/UserManagement.ts b/sdk/cosmosdb/cosmos/samples/UserManagement.ts index 5a442af36f20..b1ee339cf928 100644 --- a/sdk/cosmosdb/cosmos/samples/UserManagement.ts +++ b/sdk/cosmosdb/cosmos/samples/UserManagement.ts @@ -17,14 +17,18 @@ const item3Name = "item3"; // Establish a new instance of the DocumentDBClient to be used throughout this demo const client = new CosmosClient({ endpoint, key }); -async function run() { +async function run(): Promise { // -------------------------------------------------------------------------------------------------- // We need a database, two containers, two users, and some permissions for this sample, // So let's go ahead and set these up initially // -------------------------------------------------------------------------------------------------- const { database } = await client.databases.createIfNotExists({ id: databaseId }); - const { container: container1 } = await database.containers.createIfNotExists({ id: container1Name }); - const { container: container2 } = await database.containers.createIfNotExists({ id: container2Name }); + const { container: container1 } = await database.containers.createIfNotExists({ + id: container1Name + }); + const { container: container2 } = await database.containers.createIfNotExists({ + id: container2Name + }); let itemSpec = { id: item1Name }; @@ -32,17 +36,17 @@ async function run() { let permissionDef; - const { resource: itemDef, item: item1 } = await container1.items.create(itemSpec); + const { item: item1 } = await container1.items.create(itemSpec); console.log(item1Name + "Created in " + container1Name + " !"); itemSpec = { id: item2Name }; - const { item: item2 } = await container1.items.create(itemSpec); + await container1.items.create(itemSpec); console.log(item2Name + "Created in " + container1Name + " !"); itemSpec = { id: item3Name }; - const { item: item3 } = await container2.items.create(itemSpec); + await container2.items.create(itemSpec); console.log(item3Name + " Created in " + container2Name + " !"); const { user: user1 } = await database.users.create(userDef); @@ -62,7 +66,7 @@ async function run() { permissionDef = { id: "p2", permissionMode: PermissionMode.All, resource: item1.url }; // All Permissions on Doc1 for user1 - const { permission: permission2 } = await user1.permissions.create(permissionDef); + await user1.permissions.create(permissionDef); console.log("All permission assigned to Thomas Andersen on item 1!"); permissionDef = { id: "p3", permissionMode: PermissionMode.Read, resource: container2.url }; @@ -73,7 +77,7 @@ async function run() { permissionDef = { id: "p4", permissionMode: PermissionMode.All, resource: container2.url }; - const { permission: permission4 } = await user2.permissions.create(permissionDef); + await user2.permissions.create(permissionDef); console.log("All permission assigned to Robin Wakefield on container 2!"); const { resources: permissions } = await user1.permissions.readAll().fetchAll(); @@ -108,21 +112,30 @@ async function run() { await finish(); } -async function getResourceToken(container: Container, permission: Permission) { +async function getResourceToken( + container: Container, + permission: Permission +): Promise<{ + [x: number]: any; +}> { const { resource: permDef } = await permission.read(); return { [container.url]: permDef._token }; } -async function attemptWriteWithReadPermission(container: Container, user: User, permission: Permission) { +async function attemptWriteWithReadPermission( + container: Container, + user: User, + permission: Permission +): Promise { const resourceTokens = await getResourceToken(container, permission); - const client = new CosmosClient({ + const cosmosClient = new CosmosClient({ endpoint, resourceTokens }); const itemDef = { id: "not allowed" }; try { - await client + await cosmosClient .database(databaseId) .container(container.id) .items.upsert(itemDef); @@ -143,12 +156,12 @@ async function attemptReadFromTwoCollections( user1: User, permission1: Permission, permission2: Permission -) { +): Promise { const token1 = await getResourceToken(container1, permission1); const token2 = await getResourceToken(container2, permission2); const resourceTokens = { ...token1, ...token2 }; - const client = new CosmosClient({ + const cosmosClient = new CosmosClient({ endpoint, resourceTokens }); @@ -158,7 +171,9 @@ async function attemptReadFromTwoCollections( .container(container1.id) .items.readAll() .fetchAll(); - console.log(user1.id + " able to read items from container 1. Document count is " + items1.length); + console.log( + user1.id + " able to read items from container 1. Document count is " + items1.length + ); const { resources: items2 } = await client .database(databaseId) @@ -166,12 +181,14 @@ async function attemptReadFromTwoCollections( .items.readAll() .fetchAll(); - console.log(user1.id + " able to read items from container 2. Document count is " + items2.length); + console.log( + user1.id + " able to read items from container 2. Document count is " + items2.length + ); const itemDef = { id: "not allowed" }; try { - await client + await cosmosClient .database(databaseId) .container(container2.id) .items.upsert(itemDef); diff --git a/sdk/cosmosdb/cosmos/src/ClientContext.ts b/sdk/cosmosdb/cosmos/src/ClientContext.ts index 0afe272bb9d5..f47308a5185b 100644 --- a/sdk/cosmosdb/cosmos/src/ClientContext.ts +++ b/sdk/cosmosdb/cosmos/src/ClientContext.ts @@ -207,7 +207,7 @@ export class ClientContext { collectionLink: string, query?: string | SqlQuerySpec, options?: FeedOptions - ) { + ): QueryIterator { const path = getPathFromLink(collectionLink, ResourceType.pkranges); const id = getIdFromLink(collectionLink); const cb: FetchFunctionCallback = (innerOptions) => { @@ -334,7 +334,7 @@ export class ClientContext { } } - private applySessionToken(requestContext: RequestContext) { + private applySessionToken(requestContext: RequestContext): void { const request = this.getSessionParams(requestContext.path); if (requestContext.headers && requestContext.headers[Constants.HttpHeaders.SessionToken]) { @@ -555,7 +555,7 @@ export class ClientContext { partitionKeyRangeId: string; resourceId: string; options?: RequestOptions; - }) { + }): Promise> { try { const request: RequestContext = { globalEndpointManager: this.globalEndpointManager, @@ -597,7 +597,7 @@ export class ClientContext { path: string, operationType: OperationType, resHeaders: CosmosHeaders - ) { + ): void { const request = this.getSessionParams(path); request.operationType = operationType; if ( @@ -612,7 +612,7 @@ export class ClientContext { } } - public clearSessionToken(path: string) { + public clearSessionToken(path: string): void { const request = this.getSessionParams(path); this.sessionContainer.remove(request); } @@ -650,7 +650,7 @@ export class ClientContext { return false; } - private buildHeaders(requestContext: RequestContext) { + private buildHeaders(requestContext: RequestContext): Promise { return getHeaders({ clientOptions: this.cosmosClientOptions, defaultHeaders: { diff --git a/sdk/cosmosdb/cosmos/src/CosmosClient.ts b/sdk/cosmosdb/cosmos/src/CosmosClient.ts index 133f76f2a69f..8eb31a1faec5 100644 --- a/sdk/cosmosdb/cosmos/src/CosmosClient.ts +++ b/sdk/cosmosdb/cosmos/src/CosmosClient.ts @@ -10,7 +10,7 @@ import { CosmosClientOptions } from "./CosmosClientOptions"; import { DatabaseAccount, defaultConnectionPolicy } from "./documents"; import { GlobalEndpointManager } from "./globalEndpointManager"; import { RequestOptions, ResourceResponse } from "./request"; -import { checkURL } from "./utils/url"; +import { checkURL } from "./utils/checkURL"; /** * Provides a client-side logical representation of the Azure Cosmos DB database account. @@ -150,7 +150,7 @@ export class CosmosClient { * Used for reading, or updating a existing offer by id. * @param id - The id of the offer. */ - public offer(id: string) { + public offer(id: string): Offer { return new Offer(this, id, this.clientContext); } } diff --git a/sdk/cosmosdb/cosmos/src/auth.ts b/sdk/cosmosdb/cosmos/src/auth.ts index dd71a0ac48cb..4799808b5a9b 100644 --- a/sdk/cosmosdb/cosmos/src/auth.ts +++ b/sdk/cosmosdb/cosmos/src/auth.ts @@ -83,7 +83,7 @@ export async function setAuthorizationTokenHeaderUsingMasterKey( resourceType: ResourceType, headers: CosmosHeaders, masterKey: string -) { +): Promise { // TODO This should live in cosmos-sign if (resourceType === ResourceType.offer) { resourceId = resourceId && resourceId.toLowerCase(); @@ -102,7 +102,7 @@ export function getAuthorizationTokenUsingResourceTokens( resourceTokens: { [resourceId: string]: string }, path: string, resourceId: string -) { +): string { if (resourceTokens && Object.keys(resourceTokens).length > 0) { // For database account access(through getDatabaseAccount API), path and resourceId are "", // so in this case we return the first token to be used for creating the auth header as the diff --git a/sdk/cosmosdb/cosmos/src/client/Conflict/Conflict.ts b/sdk/cosmosdb/cosmos/src/client/Conflict/Conflict.ts index d2c93d5c3fde..400dbf02ebfb 100644 --- a/sdk/cosmosdb/cosmos/src/client/Conflict/Conflict.ts +++ b/sdk/cosmosdb/cosmos/src/client/Conflict/Conflict.ts @@ -16,7 +16,7 @@ export class Conflict { /** * Returns a reference URL to the resource. Used for linking in Permissions. */ - public get url() { + public get url(): string { return `/${this.container.url}/${Constants.Path.ConflictsPathSegment}/${this.id}`; } /** diff --git a/sdk/cosmosdb/cosmos/src/client/Container/Container.ts b/sdk/cosmosdb/cosmos/src/client/Container/Container.ts index 58a7dd1ba5aa..bf0d06ebce4c 100644 --- a/sdk/cosmosdb/cosmos/src/client/Container/Container.ts +++ b/sdk/cosmosdb/cosmos/src/client/Container/Container.ts @@ -8,7 +8,7 @@ import { isResourceValid, ResourceType } from "../../common"; -import { PartitionKeyDefinition } from "../../documents"; +import { PartitionKey, PartitionKeyDefinition } from "../../documents"; import { SqlQuerySpec } from "../../queryExecutionContext"; import { QueryIterator } from "../../queryIterator"; import { FeedOptions, RequestOptions, ResourceResponse, Response } from "../../request"; @@ -80,7 +80,7 @@ export class Container { /** * Returns a reference URL to the resource. Used for linking in Permissions. */ - public get url() { + public get url(): string { return createDocumentCollectionUri(this.database.id, this.id); } @@ -106,7 +106,7 @@ export class Container { * @example Replace an item * `const {body: replacedItem} = await container.item("", "").replace({id: "", title: "Updated post", authorID: 5});` */ - public item(id: string, partitionKeyValue?: any): Item { + public item(id: string, partitionKeyValue?: PartitionKey): Item { return new Item(this, id, partitionKeyValue, this.clientContext); } diff --git a/sdk/cosmosdb/cosmos/src/client/Container/Containers.ts b/sdk/cosmosdb/cosmos/src/client/Container/Containers.ts index 21eecce115d0..080c138c1a09 100644 --- a/sdk/cosmosdb/cosmos/src/client/Container/Containers.ts +++ b/sdk/cosmosdb/cosmos/src/client/Container/Containers.ts @@ -117,7 +117,11 @@ export class Containers { if (body.maxThroughput) { const autoscaleParams: { maxThroughput: number; - autoUpgradePolicy?: object; + autoUpgradePolicy?: { + throughputPolicy: { + incrementPercent: number; + }; + }; } = { maxThroughput: body.maxThroughput }; diff --git a/sdk/cosmosdb/cosmos/src/client/Database/Database.ts b/sdk/cosmosdb/cosmos/src/client/Database/Database.ts index dd9073a7bceb..9549ddea235c 100644 --- a/sdk/cosmosdb/cosmos/src/client/Database/Database.ts +++ b/sdk/cosmosdb/cosmos/src/client/Database/Database.ts @@ -43,7 +43,7 @@ export class Database { /** * Returns a reference URL to the resource. Used for linking in Permissions. */ - public get url() { + public get url(): string { return createDatabaseUri(this.id); } diff --git a/sdk/cosmosdb/cosmos/src/client/Database/Databases.ts b/sdk/cosmosdb/cosmos/src/client/Database/Databases.ts index 5a93e30d387a..27570aab4eb9 100644 --- a/sdk/cosmosdb/cosmos/src/client/Database/Databases.ts +++ b/sdk/cosmosdb/cosmos/src/client/Database/Databases.ts @@ -109,7 +109,11 @@ export class Databases { if (body.maxThroughput) { const autoscaleParams: { maxThroughput: number; - autoUpgradePolicy?: object; + autoUpgradePolicy?: { + throughputPolicy: { + incrementPercent: number; + }; + }; } = { maxThroughput: body.maxThroughput }; diff --git a/sdk/cosmosdb/cosmos/src/client/Item/Item.ts b/sdk/cosmosdb/cosmos/src/client/Item/Item.ts index 8255789cb23c..c9758004651a 100644 --- a/sdk/cosmosdb/cosmos/src/client/Item/Item.ts +++ b/sdk/cosmosdb/cosmos/src/client/Item/Item.ts @@ -27,7 +27,7 @@ export class Item { /** * Returns a reference URL to the resource. Used for linking in Permissions. */ - public get url() { + public get url(): string { return createDocumentUri(this.container.database.id, this.container.id, this.id); } diff --git a/sdk/cosmosdb/cosmos/src/client/Item/Items.ts b/sdk/cosmosdb/cosmos/src/client/Item/Items.ts index f10874f753cd..c60ba6dfeed1 100644 --- a/sdk/cosmosdb/cosmos/src/client/Item/Items.ts +++ b/sdk/cosmosdb/cosmos/src/client/Item/Items.ts @@ -305,7 +305,10 @@ export class Items { * @param body - Represents the body of the item. Can contain any number of user defined properties. * @param options - Used for modifying the request (for instance, specifying the partition key). */ - public async upsert(body: any, options?: RequestOptions): Promise>; + public async upsert( + body: unknown, + options?: RequestOptions + ): Promise>; /** * Upsert an item. * diff --git a/sdk/cosmosdb/cosmos/src/client/Offer/Offer.ts b/sdk/cosmosdb/cosmos/src/client/Offer/Offer.ts index c13d2df879fe..afa6aef2ee83 100644 --- a/sdk/cosmosdb/cosmos/src/client/Offer/Offer.ts +++ b/sdk/cosmosdb/cosmos/src/client/Offer/Offer.ts @@ -16,7 +16,7 @@ export class Offer { /** * Returns a reference URL to the resource. Used for linking in Permissions. */ - public get url() { + public get url(): string { return `/${Constants.Path.OffersPathSegment}/${this.id}`; } /** diff --git a/sdk/cosmosdb/cosmos/src/client/Permission/Permission.ts b/sdk/cosmosdb/cosmos/src/client/Permission/Permission.ts index 3178e7936be3..cb2846918c93 100644 --- a/sdk/cosmosdb/cosmos/src/client/Permission/Permission.ts +++ b/sdk/cosmosdb/cosmos/src/client/Permission/Permission.ts @@ -23,7 +23,7 @@ export class Permission { /** * Returns a reference URL to the resource. Used for linking in Permissions. */ - public get url() { + public get url(): string { return createPermissionUri(this.user.database.id, this.user.id, this.id); } /** diff --git a/sdk/cosmosdb/cosmos/src/client/StoredProcedure/StoredProcedure.ts b/sdk/cosmosdb/cosmos/src/client/StoredProcedure/StoredProcedure.ts index ddeae4b222db..44f58826a22e 100644 --- a/sdk/cosmosdb/cosmos/src/client/StoredProcedure/StoredProcedure.ts +++ b/sdk/cosmosdb/cosmos/src/client/StoredProcedure/StoredProcedure.ts @@ -8,6 +8,7 @@ import { isResourceValid, ResourceType } from "../../common"; +import { PartitionKey } from "../../documents/PartitionKey"; import { undefinedPartitionKey } from "../../extractPartitionKey"; import { RequestOptions, ResourceResponse } from "../../request"; import { Container } from "../Container"; @@ -23,7 +24,7 @@ export class StoredProcedure { /** * Returns a reference URL to the resource. Used for linking in Permissions. */ - public get url() { + public get url(): string { return createStoredProcedureUri(this.container.database.id, this.container.id, this.id); } /** @@ -110,7 +111,7 @@ export class StoredProcedure { * @param options - Additional options, such as the partition key to invoke the {@link StoredProcedure} on. */ public async execute( - partitionKey: any, + partitionKey: PartitionKey, params?: any[], options?: RequestOptions ): Promise> { diff --git a/sdk/cosmosdb/cosmos/src/client/Trigger/Trigger.ts b/sdk/cosmosdb/cosmos/src/client/Trigger/Trigger.ts index 45d633e60369..583f1f1c4980 100644 --- a/sdk/cosmosdb/cosmos/src/client/Trigger/Trigger.ts +++ b/sdk/cosmosdb/cosmos/src/client/Trigger/Trigger.ts @@ -22,7 +22,7 @@ export class Trigger { /** * Returns a reference URL to the resource. Used for linking in Permissions. */ - public get url() { + public get url(): string { return createTriggerUri(this.container.database.id, this.container.id, this.id); } diff --git a/sdk/cosmosdb/cosmos/src/client/User/User.ts b/sdk/cosmosdb/cosmos/src/client/User/User.ts index 533b68b84e70..1a71e69b7454 100644 --- a/sdk/cosmosdb/cosmos/src/client/User/User.ts +++ b/sdk/cosmosdb/cosmos/src/client/User/User.ts @@ -31,7 +31,7 @@ export class User { /** * Returns a reference URL to the resource. Used for linking in Permissions. */ - public get url() { + public get url(): string { return createUserUri(this.database.id, this.id); } /** diff --git a/sdk/cosmosdb/cosmos/src/client/UserDefinedFunction/UserDefinedFunction.ts b/sdk/cosmosdb/cosmos/src/client/UserDefinedFunction/UserDefinedFunction.ts index 7c6b123bf8d5..e83d8f32d63a 100644 --- a/sdk/cosmosdb/cosmos/src/client/UserDefinedFunction/UserDefinedFunction.ts +++ b/sdk/cosmosdb/cosmos/src/client/UserDefinedFunction/UserDefinedFunction.ts @@ -22,7 +22,7 @@ export class UserDefinedFunction { /** * Returns a reference URL to the resource. Used for linking in Permissions. */ - public get url() { + public get url(): string { return createUserDefinedFunctionUri(this.container.database.id, this.container.id, this.id); } /** diff --git a/sdk/cosmosdb/cosmos/src/common/constants.ts b/sdk/cosmosdb/cosmos/src/common/constants.ts index 1e3db5338156..32e80dbc4b98 100644 --- a/sdk/cosmosdb/cosmos/src/common/constants.ts +++ b/sdk/cosmosdb/cosmos/src/common/constants.ts @@ -1,6 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +export interface PartitionKeyRangePropertiesNames { + // Partition Key Range Constants + MinInclusive: "minInclusive"; + MaxExclusive: "maxExclusive"; + Id: "id"; +} + /** * @hidden */ @@ -202,7 +209,7 @@ export const Constants = { MinInclusive: "minInclusive", MaxExclusive: "maxExclusive", Id: "id" - }, + } as PartitionKeyRangePropertiesNames, QueryRangeConstants: { // Partition Key Range Constants diff --git a/sdk/cosmosdb/cosmos/src/common/helper.ts b/sdk/cosmosdb/cosmos/src/common/helper.ts index 8431b79177bf..24e28d0d3bde 100644 --- a/sdk/cosmosdb/cosmos/src/common/helper.ts +++ b/sdk/cosmosdb/cosmos/src/common/helper.ts @@ -8,7 +8,7 @@ const trimRightSlashes = new RegExp("[/]+$"); const illegalResourceIdCharacters = new RegExp("[/\\\\?#]"); /** @hidden */ -export function jsonStringifyAndEscapeNonASCII(arg: any) { +export function jsonStringifyAndEscapeNonASCII(arg: unknown): string { // TODO: better way for this? Not sure. // escapes non-ASCII characters as \uXXXX return JSON.stringify(arg).replace(/[\u007F-\uFFFF]/g, (m) => { @@ -19,7 +19,15 @@ export function jsonStringifyAndEscapeNonASCII(arg: any) { /** * @hidden */ -export function parseLink(resourcePath: string) { +export function parseLink( + resourcePath: string +): { + type: ResourceType; + objectBody: { + id: string; + self: string; + }; +} { if (resourcePath.length === 0) { /* for DatabaseAccount case, both type and objectBody will be undefined. */ return { @@ -92,7 +100,7 @@ export function sleep(time: number): Promise { /** * @hidden */ -export function getContainerLink(link: string) { +export function getContainerLink(link: string): string { return link .split("/") .slice(0, 4) @@ -102,33 +110,33 @@ export function getContainerLink(link: string) { /** * @hidden */ -export function trimSlashes(source: string) { +export function trimSlashes(source: string): string { return source.replace(trimLeftSlashes, "").replace(trimRightSlashes, ""); } /** * @hidden */ -export function getHexaDigit() { +export function getHexaDigit(): string { return Math.floor(Math.random() * 16).toString(16); } /** * @hidden */ -export function parsePath(path: string) { +export function parsePath(path: string): string[] { const pathParts = []; let currentIndex = 0; - const throwError = () => { + const throwError = (): never => { throw new Error("Path " + path + " is invalid at index " + currentIndex); }; - const getEscapedToken = () => { + const getEscapedToken = (): string => { const quote = path[currentIndex]; let newIndex = ++currentIndex; - while (true) { + for (;;) { newIndex = path.indexOf(quote, newIndex); if (newIndex === -1) { throwError(); @@ -146,7 +154,7 @@ export function parsePath(path: string) { return token; }; - const getToken = () => { + const getToken = (): string => { const newIndex = path.indexOf("/", currentIndex); let token = null; if (newIndex === -1) { @@ -183,8 +191,8 @@ export function parsePath(path: string) { /** * @hidden */ -export function isResourceValid(resource: any, err: any) { - // TODO: any TODO: code smell +export function isResourceValid(resource: { id?: string }, err: { message?: string }): boolean { + // TODO: fix strictness issues so that caller contexts respects the types of the functions if (resource.id) { if (typeof resource.id !== "string") { err.message = "Id must be a string."; @@ -209,13 +217,13 @@ export function isResourceValid(resource: any, err: any) { } /** @hidden */ -export function getIdFromLink(resourceLink: string) { +export function getIdFromLink(resourceLink: string): string { resourceLink = trimSlashes(resourceLink); return resourceLink; } /** @hidden */ -export function getPathFromLink(resourceLink: string, resourceType?: string) { +export function getPathFromLink(resourceLink: string, resourceType?: string): string { resourceLink = trimSlashes(resourceLink); if (resourceType) { return "/" + encodeURI(resourceLink) + "/" + resourceType; @@ -227,7 +235,7 @@ export function getPathFromLink(resourceLink: string, resourceType?: string) { /** * @hidden */ -export function isStringNullOrEmpty(inputString: string) { +export function isStringNullOrEmpty(inputString: string): boolean { // checks whether string is null, undefined, empty or only contains space return !inputString || /^\s*$/.test(inputString); } @@ -235,7 +243,7 @@ export function isStringNullOrEmpty(inputString: string) { /** * @hidden */ -export function trimSlashFromLeftAndRight(inputString: string) { +export function trimSlashFromLeftAndRight(inputString: string): string { if (typeof inputString !== "string") { throw new Error("invalid input: input is not string"); } @@ -246,7 +254,7 @@ export function trimSlashFromLeftAndRight(inputString: string) { /** * @hidden */ -export function validateResourceId(resourceId: string) { +export function validateResourceId(resourceId: string): boolean { // if resourceId is not a string or is empty throw an error if (typeof resourceId !== "string" || isStringNullOrEmpty(resourceId)) { throw new Error("Resource Id must be a string and cannot be undefined, null or empty"); @@ -268,7 +276,7 @@ export function validateResourceId(resourceId: string) { /** * @hidden */ -export function getResourceIdFromPath(resourcePath: string) { +export function getResourceIdFromPath(resourcePath: string): string { if (!resourcePath || typeof resourcePath !== "string") { return null; } diff --git a/sdk/cosmosdb/cosmos/src/common/logger.ts b/sdk/cosmosdb/cosmos/src/common/logger.ts index f6b9724f00de..8c8bf382bfcf 100644 --- a/sdk/cosmosdb/cosmos/src/common/logger.ts +++ b/sdk/cosmosdb/cosmos/src/common/logger.ts @@ -25,7 +25,45 @@ const levelLogger = (namespaceLogger: debugLib.Debugger, level: logLevel) => { }; /** @hidden */ -export const logger = (namespace: string) => { +export const logger = ( + namespace: string +): { + silly: ( + message: + | string + | { + [key: string]: any; + } + ) => void; + debug: ( + message: + | string + | { + [key: string]: any; + } + ) => void; + info: ( + message: + | string + | { + [key: string]: any; + } + ) => void; + warn: ( + message: + | string + | { + [key: string]: any; + } + ) => void; + error: ( + message: + | string + | { + [key: string]: any; + } + ) => void; +} => { const namespaceLogger = cosmosDebug.extend(namespace); return { silly: levelLogger(namespaceLogger, "silly"), diff --git a/sdk/cosmosdb/cosmos/src/common/partitionKeys.ts b/sdk/cosmosdb/cosmos/src/common/partitionKeys.ts index c6cf2bb0fd2c..750effeb2274 100644 --- a/sdk/cosmosdb/cosmos/src/common/partitionKeys.ts +++ b/sdk/cosmosdb/cosmos/src/common/partitionKeys.ts @@ -1,3 +1,3 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -export const DEFAULT_PARTITION_KEY_PATH = "/_partitionKey" as "/_partitionKey"; +export const DEFAULT_PARTITION_KEY_PATH = "/_partitionKey" as "/_partitionKey"; // eslint-disable-line @typescript-eslint/prefer-as-const diff --git a/sdk/cosmosdb/cosmos/src/common/platform.ts b/sdk/cosmosdb/cosmos/src/common/platform.ts index 9d744f083ed2..d60158d52244 100644 --- a/sdk/cosmosdb/cosmos/src/common/platform.ts +++ b/sdk/cosmosdb/cosmos/src/common/platform.ts @@ -6,7 +6,7 @@ import { Constants } from "./constants"; /** * @hidden */ -export function getUserAgent(suffix?: string) { +export function getUserAgent(suffix?: string): string { const ua = `${userAgent()} ${Constants.SDKName}/${Constants.SDKVersion}`; if (suffix) { return ua + " " + suffix; diff --git a/sdk/cosmosdb/cosmos/src/common/statusCodes.ts b/sdk/cosmosdb/cosmos/src/common/statusCodes.ts index d070c4a75521..2d9c83ebc135 100644 --- a/sdk/cosmosdb/cosmos/src/common/statusCodes.ts +++ b/sdk/cosmosdb/cosmos/src/common/statusCodes.ts @@ -1,55 +1,110 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. + +/** + * @hidden + */ +export interface StatusCodesType { + // Success + Ok: 200; + Created: 201; + Accepted: 202; + NoContent: 204; + NotModified: 304; + + // Client error + BadRequest: 400; + Unauthorized: 401; + Forbidden: 403; + NotFound: 404; + MethodNotAllowed: 405; + RequestTimeout: 408; + Conflict: 409; + Gone: 410; + PreconditionFailed: 412; + RequestEntityTooLarge: 413; + TooManyRequests: 429; + RetryWith: 449; + + // Server Error + InternalServerError: 500; + ServiceUnavailable: 503; + + // Operation pause and cancel. These are FAKE status codes for QOS logging purpose only. + OperationPaused: 1200; + OperationCancelled: 1201; +} + /** * @hidden */ -export const StatusCodes = { +export const StatusCodes: StatusCodesType = { // Success - Ok: 200 as 200, - Created: 201 as 201, - Accepted: 202 as 202, - NoContent: 204 as 204, - NotModified: 304 as 304, + Ok: 200, + Created: 201, + Accepted: 202, + NoContent: 204, + NotModified: 304, // Client error - BadRequest: 400 as 400, - Unauthorized: 401 as 401, - Forbidden: 403 as 403, - NotFound: 404 as 404, - MethodNotAllowed: 405 as 405, - RequestTimeout: 408 as 408, - Conflict: 409 as 409, - Gone: 410 as 410, - PreconditionFailed: 412 as 412, - RequestEntityTooLarge: 413 as 413, - TooManyRequests: 429 as 429, - RetryWith: 449 as 449, + BadRequest: 400, + Unauthorized: 401, + Forbidden: 403, + NotFound: 404, + MethodNotAllowed: 405, + RequestTimeout: 408, + Conflict: 409, + Gone: 410, + PreconditionFailed: 412, + RequestEntityTooLarge: 413, + TooManyRequests: 429, + RetryWith: 449, // Server Error - InternalServerError: 500 as 500, - ServiceUnavailable: 503 as 503, + InternalServerError: 500, + ServiceUnavailable: 503, // Operation pause and cancel. These are FAKE status codes for QOS logging purpose only. - OperationPaused: 1200 as 1200, - OperationCancelled: 1201 as 1201 + OperationPaused: 1200, + OperationCancelled: 1201 }; /** * @hidden */ -export const SubStatusCodes = { - Unknown: 0 as 0, +export interface SubStatusCodesType { + Unknown: 0; + + // 400: Bad Request Substatus + CrossPartitionQueryNotServable: 1004; + + // 410: StatusCodeType_Gone: substatus + PartitionKeyRangeGone: 1002; + + // 404: NotFound Substatus + ReadSessionNotAvailable: 1002; + + // 403: Forbidden Substatus + WriteForbidden: 3; + DatabaseAccountNotFound: 1008; +} + +/** + * @hidden + */ +export const SubStatusCodes: SubStatusCodesType = { + Unknown: 0, // 400: Bad Request Substatus - CrossPartitionQueryNotServable: 1004 as 1004, + CrossPartitionQueryNotServable: 1004, // 410: StatusCodeType_Gone: substatus - PartitionKeyRangeGone: 1002 as 1002, + PartitionKeyRangeGone: 1002, // 404: NotFound Substatus - ReadSessionNotAvailable: 1002 as 1002, + ReadSessionNotAvailable: 1002, // 403: Forbidden Substatus - WriteForbidden: 3 as 3, - DatabaseAccountNotFound: 1008 as 1008 + WriteForbidden: 3, + DatabaseAccountNotFound: 1008 }; diff --git a/sdk/cosmosdb/cosmos/src/common/uriFactory.ts b/sdk/cosmosdb/cosmos/src/common/uriFactory.ts index 4079f5d0207c..3b12b1a91821 100644 --- a/sdk/cosmosdb/cosmos/src/common/uriFactory.ts +++ b/sdk/cosmosdb/cosmos/src/common/uriFactory.ts @@ -12,7 +12,7 @@ import { trimSlashFromLeftAndRight, validateResourceId } from "./helper"; * @returns A database link in the format of `dbs/{0}` * with `{0}` being a Uri escaped version of the databaseId */ -export function createDatabaseUri(databaseId: string) { +export function createDatabaseUri(databaseId: string): string { databaseId = trimSlashFromLeftAndRight(databaseId); validateResourceId(databaseId); @@ -30,7 +30,7 @@ export function createDatabaseUri(databaseId: string) { * with `{0}` being a Uri escaped version of the databaseId and `{1}` being collectionId * @hidden */ -export function createDocumentCollectionUri(databaseId: string, collectionId: string) { +export function createDocumentCollectionUri(databaseId: string, collectionId: string): string { collectionId = trimSlashFromLeftAndRight(collectionId); validateResourceId(collectionId); @@ -49,7 +49,7 @@ export function createDocumentCollectionUri(databaseId: string, collectionId: st * with `{0}` being a Uri escaped version of the databaseId and `{1}` being userId * @hidden */ -export function createUserUri(databaseId: string, userId: string) { +export function createUserUri(databaseId: string, userId: string): string { userId = trimSlashFromLeftAndRight(userId); validateResourceId(userId); @@ -68,7 +68,11 @@ export function createUserUri(databaseId: string, userId: string) { * the databaseId, `{1}` being collectionId and `{2}` being the documentId * @hidden */ -export function createDocumentUri(databaseId: string, collectionId: string, documentId: string) { +export function createDocumentUri( + databaseId: string, + collectionId: string, + documentId: string +): string { documentId = trimSlashFromLeftAndRight(documentId); validateResourceId(documentId); @@ -91,7 +95,11 @@ export function createDocumentUri(databaseId: string, collectionId: string, docu * with `{0}` being a Uri escaped version of the databaseId, `{1}` being userId and `{2}` being permissionId * @hidden */ -export function createPermissionUri(databaseId: string, userId: string, permissionId: string) { +export function createPermissionUri( + databaseId: string, + userId: string, + permissionId: string +): string { permissionId = trimSlashFromLeftAndRight(permissionId); validateResourceId(permissionId); @@ -120,7 +128,7 @@ export function createStoredProcedureUri( databaseId: string, collectionId: string, storedProcedureId: string -) { +): string { storedProcedureId = trimSlashFromLeftAndRight(storedProcedureId); validateResourceId(storedProcedureId); @@ -144,7 +152,11 @@ export function createStoredProcedureUri( * `{1}` being collectionId and `{2}` being the triggerId * @hidden */ -export function createTriggerUri(databaseId: string, collectionId: string, triggerId: string) { +export function createTriggerUri( + databaseId: string, + collectionId: string, + triggerId: string +): string { triggerId = trimSlashFromLeftAndRight(triggerId); validateResourceId(triggerId); @@ -172,7 +184,7 @@ export function createUserDefinedFunctionUri( databaseId: string, collectionId: string, udfId: string -) { +): string { udfId = trimSlashFromLeftAndRight(udfId); validateResourceId(udfId); @@ -195,7 +207,11 @@ export function createUserDefinedFunctionUri( * with `{0}` being a Uri escaped version of the databaseId, `{1}` being collectionId and `{2}` being the conflictId * @hidden */ -export function createConflictUri(databaseId: string, collectionId: string, conflictId: string) { +export function createConflictUri( + databaseId: string, + collectionId: string, + conflictId: string +): string { conflictId = trimSlashFromLeftAndRight(conflictId); validateResourceId(conflictId); @@ -224,7 +240,7 @@ export function createAttachmentUri( collectionId: string, documentId: string, attachmentId: string -) { +): string { attachmentId = trimSlashFromLeftAndRight(attachmentId); validateResourceId(attachmentId); @@ -246,7 +262,7 @@ export function createAttachmentUri( * `dbs/{0}/colls/{1}/pkranges` with `{0}` being a Uri escaped version of the databaseId and `{1}` being collectionId * @hidden */ -export function createPartitionKeyRangesUri(databaseId: string, collectionId: string) { +export function createPartitionKeyRangesUri(databaseId: string, collectionId: string): string { return ( createDocumentCollectionUri(databaseId, collectionId) + "/" + diff --git a/sdk/cosmosdb/cosmos/src/documents/DatabaseAccount.ts b/sdk/cosmosdb/cosmos/src/documents/DatabaseAccount.ts index 19f679295cd3..a4c2232ffc02 100644 --- a/sdk/cosmosdb/cosmos/src/documents/DatabaseAccount.ts +++ b/sdk/cosmosdb/cosmos/src/documents/DatabaseAccount.ts @@ -16,7 +16,7 @@ export class DatabaseAccount { * The self-link for Databases in the databaseAccount. * @deprecated Use `databasesLink` */ - public get DatabasesLink() { + public get DatabasesLink(): string { return this.databasesLink; } /** The self-link for Databases in the databaseAccount. */ @@ -25,7 +25,7 @@ export class DatabaseAccount { * The self-link for Media in the databaseAccount. * @deprecated Use `mediaLink` */ - public get MediaLink() { + public get MediaLink(): string { return this.mediaLink; } /** The self-link for Media in the databaseAccount. */ @@ -34,7 +34,7 @@ export class DatabaseAccount { * Attachment content (media) storage quota in MBs ( Retrieved from gateway ). * @deprecated use `maxMediaStorageUsageInMB` */ - public get MaxMediaStorageUsageInMB() { + public get MaxMediaStorageUsageInMB(): number { return this.maxMediaStorageUsageInMB; } /** Attachment content (media) storage quota in MBs ( Retrieved from gateway ). */ @@ -47,7 +47,7 @@ export class DatabaseAccount { * * @deprecated use `currentMediaStorageUsageInMB` */ - public get CurrentMediaStorageUsageInMB() { + public get CurrentMediaStorageUsageInMB(): number { return this.currentMediaStorageUsageInMB; } /** @@ -61,7 +61,7 @@ export class DatabaseAccount { * Gets the UserConsistencyPolicy settings. * @deprecated use `consistencyPolicy` */ - public get ConsistencyPolicy() { + public get ConsistencyPolicy(): ConsistencyLevel { return this.consistencyPolicy; } /** Gets the UserConsistencyPolicy settings. */ diff --git a/sdk/cosmosdb/cosmos/src/documents/PartitionKey.ts b/sdk/cosmosdb/cosmos/src/documents/PartitionKey.ts index a8d57d81581f..be19cd7d496d 100644 --- a/sdk/cosmosdb/cosmos/src/documents/PartitionKey.ts +++ b/sdk/cosmosdb/cosmos/src/documents/PartitionKey.ts @@ -2,4 +2,4 @@ // Licensed under the MIT license. import { PartitionKeyDefinition } from "./PartitionKeyDefinition"; -export type PartitionKey = PartitionKeyDefinition | string | number | {}; +export type PartitionKey = PartitionKeyDefinition | string | number | unknown; diff --git a/sdk/cosmosdb/cosmos/src/extractPartitionKey.ts b/sdk/cosmosdb/cosmos/src/extractPartitionKey.ts index bd00a9a4ea2a..e4ef2c425742 100644 --- a/sdk/cosmosdb/cosmos/src/extractPartitionKey.ts +++ b/sdk/cosmosdb/cosmos/src/extractPartitionKey.ts @@ -7,7 +7,7 @@ import { PartitionKey, PartitionKeyDefinition } from "./documents"; * @hidden */ export function extractPartitionKey( - document: any, + document: unknown, partitionKeyDefinition: PartitionKeyDefinition ): PartitionKey[] { if ( @@ -20,11 +20,12 @@ export function extractPartitionKey( const pathParts = parsePath(path); let obj = document; for (const part of pathParts) { - if (!(typeof obj === "object" && part in obj)) { + if (typeof obj === "object" && part in obj) { + obj = (obj as Record)[part]; + } else { obj = undefined; break; } - obj = obj[part]; } partitionKey.push(obj); }); @@ -37,7 +38,7 @@ export function extractPartitionKey( /** * @hidden */ -export function undefinedPartitionKey(partitionKeyDefinition: PartitionKeyDefinition) { +export function undefinedPartitionKey(partitionKeyDefinition: PartitionKeyDefinition): unknown[] { if (partitionKeyDefinition.systemKey === true) { return []; } else { diff --git a/sdk/cosmosdb/cosmos/src/globalEndpointManager.ts b/sdk/cosmosdb/cosmos/src/globalEndpointManager.ts index a05fe961a700..ca5593f8bdc6 100644 --- a/sdk/cosmosdb/cosmos/src/globalEndpointManager.ts +++ b/sdk/cosmosdb/cosmos/src/globalEndpointManager.ts @@ -66,7 +66,7 @@ export class GlobalEndpointManager { return this.writeableLocations.map((loc) => loc.databaseAccountEndpoint); } - public async markCurrentLocationUnavailableForRead(endpoint: string) { + public async markCurrentLocationUnavailableForRead(endpoint: string): Promise { await this.refreshEndpointList(); const location = this.readableLocations.find((loc) => loc.databaseAccountEndpoint === endpoint); if (location) { @@ -74,7 +74,7 @@ export class GlobalEndpointManager { } } - public async markCurrentLocationUnavailableForWrite(endpoint: string) { + public async markCurrentLocationUnavailableForWrite(endpoint: string): Promise { await this.refreshEndpointList(); const location = this.writeableLocations.find( (loc) => loc.databaseAccountEndpoint === endpoint @@ -100,7 +100,10 @@ export class GlobalEndpointManager { return canUse; } - public async resolveServiceEndpoint(resourceType: ResourceType, operationType: OperationType) { + public async resolveServiceEndpoint( + resourceType: ResourceType, + operationType: OperationType + ): Promise { // If endpoint discovery is disabled, always use the user provided endpoint if (!this.options.connectionPolicy.enableEndpointDiscovery) { return this.defaultEndpoint; @@ -165,7 +168,7 @@ export class GlobalEndpointManager { } } - private refreshEndpoints(databaseAccount: DatabaseAccount) { + private refreshEndpoints(databaseAccount: DatabaseAccount): void { for (const location of databaseAccount.writableLocations) { const existingLocation = this.writeableLocations.find((loc) => loc.name === location.name); if (!existingLocation) { @@ -225,7 +228,7 @@ export class GlobalEndpointManager { * @param defaultEndpoint - The default endpoint to use for the endpoint. * @param locationName - The location name for the azure region like "East US". */ - private static getLocationalEndpoint(defaultEndpoint: string, locationName: string) { + private static getLocationalEndpoint(defaultEndpoint: string, locationName: string): string { // For defaultEndpoint like 'https://contoso.documents.azure.com:443/' parse it to generate URL format // This defaultEndpoint should be global endpoint(and cannot be a locational endpoint) // and we agreed to document that @@ -258,7 +261,7 @@ export class GlobalEndpointManager { } } -function normalizeEndpoint(endpoint: string) { +function normalizeEndpoint(endpoint: string): string { return endpoint .split(" ") .join("") diff --git a/sdk/cosmosdb/cosmos/src/index.ts b/sdk/cosmosdb/cosmos/src/index.ts index 882f7875a25a..a0e31c4d0abd 100644 --- a/sdk/cosmosdb/cosmos/src/index.ts +++ b/sdk/cosmosdb/cosmos/src/index.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. export { DEFAULT_PARTITION_KEY_PATH } from "./common/partitionKeys"; -export { StatusCodes } from "./common"; +export { StatusCodes, StatusCodesType, PartitionKeyRangePropertiesNames } from "./common"; export { extractPartitionKey } from "./extractPartitionKey"; export { setAuthorizationTokenHeaderUsingMasterKey } from "./auth"; export { diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/AverageAggregator.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/AverageAggregator.ts index 15d3a6586cdd..f0f68ac54037 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/AverageAggregator.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/AverageAggregator.ts @@ -15,7 +15,7 @@ export class AverageAggregator implements Aggregator { /** * Add the provided item to aggregation result. */ - public aggregate(other: AverageAggregateResult) { + public aggregate(other: AverageAggregateResult): void { if (other == null || other.sum == null) { return; } @@ -30,7 +30,7 @@ export class AverageAggregator implements Aggregator { /** * Get the aggregation result. */ - public getResult() { + public getResult(): number { if (this.sum == null || this.count <= 0) { return undefined; } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/CountAggregator.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/CountAggregator.ts index f4aeb3c4e373..8c34268d79d7 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/CountAggregator.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/CountAggregator.ts @@ -15,14 +15,14 @@ export class CountAggregator implements Aggregator { /** * Add the provided item to aggregation result. */ - public aggregate(other: number) { + public aggregate(other: number): void { this.value += other; } /** * Get the aggregation result. */ - public getResult() { + public getResult(): number { return this.value; } } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/MaxAggregator.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/MaxAggregator.ts index 8ad4b6590171..847d57de784e 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/MaxAggregator.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/MaxAggregator.ts @@ -23,7 +23,7 @@ export class MaxAggregator implements Aggregator { /** * Add the provided item to aggregation result. */ - public aggregate(other: MaxAggregateResult) { + public aggregate(other: MaxAggregateResult): void { if (this.value === undefined) { this.value = other.max; } else if ( @@ -36,7 +36,7 @@ export class MaxAggregator implements Aggregator { /** * Get the aggregation result. */ - public getResult() { + public getResult(): number { return this.value; } } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/MinAggregator.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/MinAggregator.ts index 455b608d2b81..2de63e3587e9 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/MinAggregator.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/MinAggregator.ts @@ -23,7 +23,7 @@ export class MinAggregator implements Aggregator { /** * Add the provided item to aggregation result. */ - public aggregate(other: MinAggregateResult) { + public aggregate(other: MinAggregateResult): void { if (this.value === undefined) { // || typeof this.value === "object" this.value = other.min; @@ -39,7 +39,7 @@ export class MinAggregator implements Aggregator { /** * Get the aggregation result. */ - public getResult() { + public getResult(): number { return this.value; } } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/StaticValueAggregator.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/StaticValueAggregator.ts index ed8ddcdd1192..547a86f290f4 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/StaticValueAggregator.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/StaticValueAggregator.ts @@ -5,13 +5,13 @@ import { Aggregator } from "./Aggregator"; /** @hidden */ export class StaticValueAggregator implements Aggregator { public value: any; - public aggregate(other: any) { + public aggregate(other: unknown): void { if (this.value === undefined) { this.value = other; } } - public getResult() { + public getResult(): any { return this.value; } } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/SumAggregator.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/SumAggregator.ts index cff70c8804d3..874a566f3677 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/SumAggregator.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/SumAggregator.ts @@ -8,7 +8,7 @@ export class SumAggregator implements Aggregator { /** * Add the provided item to aggregation result. */ - public aggregate(other: number) { + public aggregate(other: number): void { if (other === undefined) { return; } @@ -22,7 +22,7 @@ export class SumAggregator implements Aggregator { /** * Get the aggregation result. */ - public getResult() { + public getResult(): number { return this.sum; } } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/index.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/index.ts index ad4daf6507ef..177c04e935cd 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/index.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/Aggregators/index.ts @@ -8,7 +8,15 @@ import { SumAggregator } from "./SumAggregator"; import { StaticValueAggregator } from "./StaticValueAggregator"; import { AggregateType } from "../../request/ErrorResponse"; -export function createAggregator(aggregateType: AggregateType) { +export function createAggregator( + aggregateType: AggregateType +): + | AverageAggregator + | CountAggregator + | MaxAggregator + | MinAggregator + | SumAggregator + | StaticValueAggregator { switch (aggregateType) { case "Average": return new AverageAggregator(); diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/GroupByEndpointComponent.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/GroupByEndpointComponent.ts index 6928af0c5927..427cb50ec3a8 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/GroupByEndpointComponent.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/GroupByEndpointComponent.ts @@ -87,7 +87,7 @@ export class GroupByEndpointComponent implements ExecutionContext { return { result: this.aggregateResultArray.pop(), headers: aggregateHeaders }; } - public hasMoreResults() { + public hasMoreResults(): boolean { return this.executionContext.hasMoreResults() || this.aggregateResultArray.length > 0; } } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/GroupByValueEndpointComponent.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/GroupByValueEndpointComponent.ts index 2a3308e66c99..0c54e304a824 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/GroupByValueEndpointComponent.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/GroupByValueEndpointComponent.ts @@ -91,7 +91,7 @@ export class GroupByValueEndpointComponent implements ExecutionContext { return { result: this.aggregateResultArray.pop(), headers: aggregateHeaders }; } - public hasMoreResults() { + public hasMoreResults(): boolean { return this.executionContext.hasMoreResults() || this.aggregateResultArray.length > 0; } } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/OffsetLimitEndpointComponent.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/OffsetLimitEndpointComponent.ts index 1fa80686afab..98e215e22c20 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/OffsetLimitEndpointComponent.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/OffsetLimitEndpointComponent.ts @@ -30,7 +30,7 @@ export class OffsetLimitEndpointComponent implements ExecutionContext { return { result: undefined, headers: getInitialHeader() }; } - public hasMoreResults() { + public hasMoreResults(): boolean { return (this.offset > 0 || this.limit > 0) && this.executionContext.hasMoreResults(); } } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/OrderByEndpointComponent.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/OrderByEndpointComponent.ts index f3e8171f1ca8..a11ae6b87060 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/OrderByEndpointComponent.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/OrderByEndpointComponent.ts @@ -28,7 +28,7 @@ export class OrderByEndpointComponent implements ExecutionContext { * Determine if there are still remaining resources to processs. * @returns true if there is other elements to process in the OrderByEndpointComponent. */ - public hasMoreResults() { + public hasMoreResults(): boolean { return this.executionContext.hasMoreResults(); } } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/OrderedDistinctEndpointComponent.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/OrderedDistinctEndpointComponent.ts index 7f6a31d4ca3a..cffabb769c4d 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/OrderedDistinctEndpointComponent.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/OrderedDistinctEndpointComponent.ts @@ -21,7 +21,7 @@ export class OrderedDistinctEndpointComponent implements ExecutionContext { return { result, headers }; } - public hasMoreResults() { + public hasMoreResults(): boolean { return this.executionContext.hasMoreResults(); } } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/UnorderedDistinctEndpointComponent.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/UnorderedDistinctEndpointComponent.ts index b28e60be14e0..4f4d876f47a7 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/UnorderedDistinctEndpointComponent.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/UnorderedDistinctEndpointComponent.ts @@ -23,7 +23,7 @@ export class UnorderedDistinctEndpointComponent implements ExecutionContext { return { result, headers }; } - public hasMoreResults() { + public hasMoreResults(): boolean { return this.executionContext.hasMoreResults(); } } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/emptyGroup.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/emptyGroup.ts index 6ed5da09d372..28fe3967682a 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/emptyGroup.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/EndpointComponent/emptyGroup.ts @@ -7,5 +7,5 @@ export const emptyGroup = "__empty__"; // Newer API versions rewrite the query to return `item2`. It fixes some legacy issues with the original `item` result // Aggregator code should use item2 when available -export const extractAggregateResult = (payload: any) => +export const extractAggregateResult = (payload: { item2?: unknown; item: unknown }): any => Object.keys(payload).length > 0 ? (payload.item2 ? payload.item2 : payload.item) : null; diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/FetchResult.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/FetchResult.ts index 8df91bb0af5e..b665ea3c4cbd 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/FetchResult.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/FetchResult.ts @@ -20,7 +20,7 @@ export class FetchResult { * @param error - The exception meant to be buffered on an unsuccessful fetch * @hidden */ - constructor(feedResponse: any, error: any) { + constructor(feedResponse: unknown, error: unknown) { // TODO: feedResponse/error if (feedResponse) { this.feedResponse = feedResponse; diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/defaultQueryExecutionContext.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/defaultQueryExecutionContext.ts index 758bb87f9a34..f3590cd428d1 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/defaultQueryExecutionContext.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/defaultQueryExecutionContext.ts @@ -29,7 +29,7 @@ export class DefaultQueryExecutionContext implements ExecutionContext { private fetchFunctions: FetchFunctionCallback[]; private options: FeedOptions; // TODO: any options public continuationToken: string; // TODO: any continuation - public get continuation() { + public get continuation(): string { return this.continuationToken; } private state: STATES; @@ -45,8 +45,10 @@ export class DefaultQueryExecutionContext implements ExecutionContext { * An array of functions may be used to query more than one partition. * @hidden */ - constructor(options: any, fetchFunctions: FetchFunctionCallback | FetchFunctionCallback[]) { - // TODO: any options + constructor( + options: FeedOptions, + fetchFunctions: FetchFunctionCallback | FetchFunctionCallback[] + ) { this.resources = []; this.currentIndex = 0; this.currentPartitionIndex = 0; @@ -100,7 +102,7 @@ export class DefaultQueryExecutionContext implements ExecutionContext { * * @returns true if there is other elements to process in the DefaultQueryExecutionContext. */ - public hasMoreResults() { + public hasMoreResults(): boolean { return ( this.state === DefaultQueryExecutionContext.STATES.start || this.continuationToken !== undefined || @@ -199,7 +201,7 @@ export class DefaultQueryExecutionContext implements ExecutionContext { return { result: resources, headers: responseHeaders }; } - private _canFetchMore() { + private _canFetchMore(): boolean { const res = this.state === DefaultQueryExecutionContext.STATES.start || (this.continuationToken && this.state === DefaultQueryExecutionContext.STATES.inProgress) || diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/documentProducer.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/documentProducer.ts index b5bd29a65497..e8d941ccd12b 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/documentProducer.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/documentProducer.ts @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import { PartitionKeyRange, Resource } from "../client"; import { ClientContext } from "../ClientContext"; import { Constants, @@ -14,13 +15,13 @@ import { Response } from "../request"; import { DefaultQueryExecutionContext } from "./defaultQueryExecutionContext"; import { FetchResult, FetchResultType } from "./FetchResult"; import { CosmosHeaders, getInitialHeader, mergeHeaders } from "./headerUtils"; -import { FetchFunctionCallback, SqlQuerySpec } from "./index"; +import { SqlQuerySpec } from "./index"; /** @hidden */ export class DocumentProducer { private collectionLink: string; private query: string | SqlQuerySpec; - public targetPartitionKeyRange: any; // TODO: any partitionkeyrange + public targetPartitionKeyRange: PartitionKeyRange; public fetchResults: FetchResult[]; public allFetched: boolean; private err: Error; @@ -42,7 +43,7 @@ export class DocumentProducer { private clientContext: ClientContext, collectionLink: string, query: SqlQuerySpec, - targetPartitionKeyRange: any, // TODO: any partition key range + targetPartitionKeyRange: PartitionKeyRange, options: FeedOptions ) { // TODO: any options @@ -66,7 +67,7 @@ export class DocumentProducer { * @returns buffered current items if any * @hidden */ - public peekBufferedItems() { + public peekBufferedItems(): any[] { const bufferedResults = []; for (let i = 0, done = false; i < this.fetchResults.length && !done; i++) { const fetchResult = this.fetchResults[i]; @@ -85,26 +86,31 @@ export class DocumentProducer { return bufferedResults; } - public fetchFunction: FetchFunctionCallback = async (options: any) => { + public fetchFunction = async (options: FeedOptions): Promise> => { + // eslint-disable-next-line no-invalid-this const path = getPathFromLink(this.collectionLink, ResourceType.item); + // eslint-disable-next-line no-invalid-this const id = getIdFromLink(this.collectionLink); + // eslint-disable-next-line no-invalid-this return this.clientContext.queryFeed({ path, resourceType: ResourceType.item, resourceId: id, resultFn: (result: any) => result.Documents, + // eslint-disable-next-line no-invalid-this query: this.query, options, + // eslint-disable-next-line no-invalid-this partitionKeyRangeId: this.targetPartitionKeyRange["id"] }); }; - public hasMoreResults() { + public hasMoreResults(): boolean { return this.internalExecutionContext.hasMoreResults() || this.fetchResults.length !== 0; } - public gotSplit() { + public gotSplit(): boolean { const fetchResult = this.fetchResults[0]; if (fetchResult.fetchResultType === FetchResultType.Exception) { if (DocumentProducer._needPartitionKeyRangeCacheRefresh(fetchResult.error)) { @@ -115,13 +121,13 @@ export class DocumentProducer { return false; } - private _getAndResetActiveResponseHeaders() { + private _getAndResetActiveResponseHeaders(): CosmosHeaders { const ret = this.respHeaders; this.respHeaders = getInitialHeader(); return ret; } - private _updateStates(err: any, allFetched: boolean) { + private _updateStates(err: any, allFetched: boolean): void { // TODO: any Error if (err) { this.err = err; @@ -138,7 +144,7 @@ export class DocumentProducer { this.continuationToken = this.internalExecutionContext.continuationToken; } - private static _needPartitionKeyRangeCacheRefresh(error: any) { + private static _needPartitionKeyRangeCacheRefresh(error: any): boolean { // TODO: error return ( error.code === StatusCodes.Gone && @@ -204,7 +210,7 @@ export class DocumentProducer { * @returns buffered current item if any * @hidden */ - public getTargetParitionKeyRange() { + public getTargetParitionKeyRange(): PartitionKeyRange { return this.targetPartitionKeyRange; } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/headerUtils.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/headerUtils.ts index 523a9078dc61..4e2b55adb971 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/headerUtils.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/headerUtils.ts @@ -42,7 +42,7 @@ export function getInitialHeader(): CosmosHeaders { * @hidden */ // TODO: The name of this method isn't very accurate to what it does -export function mergeHeaders(headers: CosmosHeaders, toBeMergedHeaders: CosmosHeaders) { +export function mergeHeaders(headers: CosmosHeaders, toBeMergedHeaders: CosmosHeaders): void { if (headers[Constants.HttpHeaders.RequestCharge] === undefined) { headers[Constants.HttpHeaders.RequestCharge] = 0; } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/orderByDocumentProducerComparator.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/orderByDocumentProducerComparator.ts index 591a3f2303e7..de11ae3be79e 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/orderByDocumentProducerComparator.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/orderByDocumentProducerComparator.ts @@ -40,13 +40,13 @@ export class OrderByDocumentProducerComparator { private targetPartitionKeyRangeDocProdComparator( docProd1: DocumentProducer, docProd2: DocumentProducer - ) { + ): 0 | 1 | -1 { const a = docProd1.getTargetParitionKeyRange()["minInclusive"]; const b = docProd2.getTargetParitionKeyRange()["minInclusive"]; return a === b ? 0 : a > b ? 1 : -1; } - public compare(docProd1: DocumentProducer, docProd2: DocumentProducer) { + public compare(docProd1: DocumentProducer, docProd2: DocumentProducer): number { // Need to check for split, since we don't want to dereference "item" of undefined / exception if (docProd1.gotSplit()) { return -1; @@ -79,7 +79,7 @@ export class OrderByDocumentProducerComparator { } // TODO: This smells funny - public compareValue(item1: any, type1: string, item2: any, type2: string) { + public compareValue(item1: unknown, type1: string, item2: unknown, type2: string): number { if (type1 === "object" || type2 === "object") { throw new Error("Tried to compare an object type"); } @@ -109,13 +109,13 @@ export class OrderByDocumentProducerComparator { return compFunc(item1, item2); } - private compareOrderByItem(orderByItem1: any, orderByItem2: any) { + private compareOrderByItem(orderByItem1: any, orderByItem2: any): number { const type1 = this.getType(orderByItem1); const type2 = this.getType(orderByItem2); return this.compareValue(orderByItem1["item"], type1, orderByItem2["item"], type2); } - private validateOrderByItems(res1: string[], res2: string[]) { + private validateOrderByItems(res1: string[], res2: string[]): void { if (res1.length !== res2.length) { throw new Error(`Expected ${res1.length}, but got ${res2.length}.`); } @@ -134,7 +134,18 @@ export class OrderByDocumentProducerComparator { } } - private getType(orderByItem: any) { + private getType( + orderByItem: any + ): + | "string" + | "number" + | "bigint" + | "boolean" + | "symbol" + | "undefined" + | "object" + | "function" + | "NoValue" { // TODO: any item? if (orderByItem === undefined || orderByItem.item === undefined) { return "NoValue"; @@ -146,7 +157,7 @@ export class OrderByDocumentProducerComparator { return type; } - private getOrderByItems(res: any) { + private getOrderByItems(res: any): any { // TODO: any res? return res["orderByItems"]; } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/orderByQueryExecutionContext.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/orderByQueryExecutionContext.ts index 255e62b92c08..669a8c68d6ec 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/orderByQueryExecutionContext.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/orderByQueryExecutionContext.ts @@ -2,10 +2,12 @@ // Licensed under the MIT license. import { ClientContext } from "../ClientContext"; import { PartitionedQueryExecutionInfo } from "../request/ErrorResponse"; +import { FeedOptions } from "../request/FeedOptions"; import { DocumentProducer } from "./documentProducer"; import { ExecutionContext } from "./ExecutionContext"; import { OrderByDocumentProducerComparator } from "./orderByDocumentProducerComparator"; import { ParallelQueryExecutionContextBase } from "./parallelQueryExecutionContextBase"; +import { SqlQuerySpec } from "./SqlQuerySpec"; /** @hidden */ export class OrderByQueryExecutionContext extends ParallelQueryExecutionContextBase @@ -27,8 +29,8 @@ export class OrderByQueryExecutionContext extends ParallelQueryExecutionContextB constructor( clientContext: ClientContext, collectionLink: string, - query: any, // TODO: any query - options: any, // TODO: any options + query: string | SqlQuerySpec, + options: FeedOptions, partitionedQueryExecutionInfo: PartitionedQueryExecutionInfo ) { // Calling on base class constructor @@ -43,7 +45,7 @@ export class OrderByQueryExecutionContext extends ParallelQueryExecutionContextB * @returns Comparator Function * @hidden */ - public documentProducerComparator(docProd1: DocumentProducer, docProd2: DocumentProducer) { + public documentProducerComparator(docProd1: DocumentProducer, docProd2: DocumentProducer): any { return this.orderByComparator.compare(docProd1, docProd2); } } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/parallelQueryExecutionContext.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/parallelQueryExecutionContext.ts index 50df3df05a87..fbbbed333c98 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/parallelQueryExecutionContext.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/parallelQueryExecutionContext.ts @@ -1,34 +1,17 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { ClientContext } from "../ClientContext"; -import { PartitionedQueryExecutionInfo } from "../request/ErrorResponse"; + import { DocumentProducer } from "./documentProducer"; import { ExecutionContext } from "./ExecutionContext"; import { ParallelQueryExecutionContextBase } from "./parallelQueryExecutionContextBase"; -/** @hidden */ +/** + * Provides the ParallelQueryExecutionContext. + * This class is capable of handling parallelized queries and derives from ParallelQueryExecutionContextBase. + * @hidden + */ export class ParallelQueryExecutionContext extends ParallelQueryExecutionContextBase implements ExecutionContext { - /** - * Provides the ParallelQueryExecutionContext. - * This class is capable of handling parallelized queries and dervives from ParallelQueryExecutionContextBase. - * - * @param clientContext - The service endpoint to use to create the client. - * @param collectionLink - The Collection Link - * @param options - Represents the feed options. - * @param partitionedQueryExecutionInfo - PartitionedQueryExecutionInfo - * @hidden - */ - constructor( - clientContext: ClientContext, - collectionLink: string, - query: any, - options: any, - partitionedQueryExecutionInfo: PartitionedQueryExecutionInfo - ) { - // Calling on base class constructor - super(clientContext, collectionLink, query, options, partitionedQueryExecutionInfo); - } // Instance members are inherited // Overriding documentProducerComparator for ParallelQueryExecutionContexts @@ -37,7 +20,10 @@ export class ParallelQueryExecutionContext extends ParallelQueryExecutionContext * @returns Comparator Function * @hidden */ - public documentProducerComparator(docProd1: DocumentProducer, docProd2: DocumentProducer) { + public documentProducerComparator( + docProd1: DocumentProducer, + docProd2: DocumentProducer + ): number { return docProd1.generation - docProd2.generation; } } diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/parallelQueryExecutionContextBase.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/parallelQueryExecutionContextBase.ts index db923d23b19d..66515d83ab6e 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/parallelQueryExecutionContextBase.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/parallelQueryExecutionContextBase.ts @@ -13,6 +13,7 @@ import { CosmosHeaders } from "./CosmosHeaders"; import { DocumentProducer } from "./documentProducer"; import { ExecutionContext } from "./ExecutionContext"; import { getInitialHeader, mergeHeaders } from "./headerUtils"; +import { SqlQuerySpec } from "./SqlQuerySpec"; /** @hidden */ const log = logger("parallelQueryExecutionContextBase"); @@ -52,7 +53,7 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont constructor( private clientContext: ClientContext, private collectionLink: string, - private query: any, // TODO: any - It's not SQLQuerySpec + private query: string | SqlQuerySpec, private options: FeedOptions, private partitionedQueryExecutionInfo: PartitionedQueryExecutionInfo ) { @@ -80,7 +81,7 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont this.sem = semaphore(1); // Creating callback for semaphore // TODO: Code smell - const createDocumentProducersAndFillUpPriorityQueueFunc = async () => { + const createDocumentProducersAndFillUpPriorityQueueFunc = async (): Promise => { // ensure the lock is released after finishing up try { const targetPartitionRanges = await this._onTargetPartitionRanges(); @@ -119,9 +120,9 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont }); // Fill up our priority queue with documentProducers - targetPartitionQueryExecutionContextList.forEach((documentProducer) => { + targetPartitionQueryExecutionContextList.forEach((documentProducer): void => { // has async callback - const throttledFunc = async () => { + const throttledFunc = async (): Promise => { try { const { result: document, headers } = await documentProducer.current(); this._mergeWithActiveResponseHeaders(headers); @@ -160,7 +161,7 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont dp2: DocumentProducer ): number; - private _decrementInitiationLock() { + private _decrementInitiationLock(): void { // decrements waitingForInternalExecutionContexts // if waitingForInternalExecutionContexts reaches 0 releases the semaphore and changes the state this.waitingForInternalExecutionContexts = this.waitingForInternalExecutionContexts - 1; @@ -172,17 +173,17 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont } } - private _mergeWithActiveResponseHeaders(headers: CosmosHeaders) { + private _mergeWithActiveResponseHeaders(headers: CosmosHeaders): void { mergeHeaders(this.respHeaders, headers); } - private _getAndResetActiveResponseHeaders() { + private _getAndResetActiveResponseHeaders(): CosmosHeaders { const ret = this.respHeaders; this.respHeaders = getInitialHeader(); return ret; } - private async _onTargetPartitionRanges() { + private async _onTargetPartitionRanges(): Promise { // invokes the callback when the target partition ranges are ready const parsedRanges = this.partitionedQueryExecutionInfo.queryRanges; const queryRanges = parsedRanges.map((item) => QueryRange.parseFromDict(item)); @@ -192,7 +193,9 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont /** * Gets the replacement ranges for a partitionkeyrange that has been split */ - private async _getReplacementPartitionKeyRanges(documentProducer: DocumentProducer) { + private async _getReplacementPartitionKeyRanges( + documentProducer: DocumentProducer + ): Promise { const partitionKeyRange = documentProducer.targetPartitionKeyRange; // Download the new routing map this.routingProvider = new SmartRoutingMapProvider(this.clientContext); @@ -207,7 +210,7 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont * replaces that document producer with child document producers, * then reexecutes the originFunction with the corrrected executionContext */ - private async _repairExecutionContext(originFunction: any) { + private async _repairExecutionContext(originFunction: any): Promise { // TODO: any // Get the replacement ranges // Removing the invalid documentProducer from the orderByPQ @@ -230,7 +233,7 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont const checkAndEnqueueDocumentProducer = async ( documentProducerToCheck: DocumentProducer, checkNextDocumentProducerCallback: any - ) => { + ): Promise => { try { const { result: afterItem } = await documentProducerToCheck.current(); if (afterItem === undefined) { @@ -246,7 +249,7 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont return; } }; - const checkAndEnqueueDocumentProducers = async (rdp: DocumentProducer[]) => { + const checkAndEnqueueDocumentProducers = async (rdp: DocumentProducer[]): Promise => { if (rdp.length > 0) { // We still have a replacementDocumentProducer to check const replacementDocumentProducer = rdp.shift(); @@ -266,7 +269,7 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont } } - private static _needPartitionKeyRangeCacheRefresh(error: any) { + private static _needPartitionKeyRangeCacheRefresh(error: any): boolean { // TODO: any error return ( error.code === StatusCodes.Gone && @@ -280,7 +283,7 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont * if so it repairs the execution context and executes the ifCallback, * else it continues with the current execution context and executes the elseCallback */ - private async _repairExecutionContextIfNeeded(ifCallback: any, elseCallback: any) { + private async _repairExecutionContextIfNeeded(ifCallback: any, elseCallback: any): Promise { const documentProducer = this.orderByPQ.peek(); // Check if split happened try { @@ -329,13 +332,13 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont }); } - const ifCallback = () => { + const ifCallback = (): void => { // Release the semaphore to avoid deadlock this.sem.leave(); // Reexcute the function return resolve(this.nextItem()); }; - const elseCallback = async () => { + const elseCallback = async (): Promise => { let documentProducer: DocumentProducer; try { documentProducer = this.orderByPQ.deq(); @@ -436,7 +439,7 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont * token or the elements remaining on the current batch in the QueryIterator. * @returns true if there is other elements to process in the ParallelQueryExecutionContextBase. */ - public hasMoreResults() { + public hasMoreResults(): boolean { return !( this.state === ParallelQueryExecutionContextBase.STATES.ended || this.err !== undefined ); @@ -448,21 +451,24 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont private _createTargetPartitionQueryExecutionContext( partitionKeyTargetRange: any, continuationToken?: any - ) { + ): DocumentProducer { // TODO: any // creates target partition range Query Execution Context let rewrittenQuery = this.partitionedQueryExecutionInfo.queryInfo.rewrittenQuery; - let query = this.query; + let sqlQuerySpec: SqlQuerySpec; + const query = this.query; if (typeof query === "string") { - query = { query }; + sqlQuerySpec = { query }; + } else { + sqlQuerySpec = query; } const formatPlaceHolder = "{documentdb-formattableorderbyquery-filter}"; if (rewrittenQuery) { - query = JSON.parse(JSON.stringify(query)); + sqlQuerySpec = JSON.parse(JSON.stringify(sqlQuerySpec)); // We hardcode the formattable filter to true for now rewrittenQuery = rewrittenQuery.replace(formatPlaceHolder, "true"); - query["query"] = rewrittenQuery; + sqlQuerySpec["query"] = rewrittenQuery; } const options = JSON.parse(JSON.stringify(this.options)); @@ -471,7 +477,7 @@ export abstract class ParallelQueryExecutionContextBase implements ExecutionCont return new DocumentProducer( this.clientContext, this.collectionLink, - query, + sqlQuerySpec, partitionKeyTargetRange, options ); diff --git a/sdk/cosmosdb/cosmos/src/queryExecutionContext/pipelinedQueryExecutionContext.ts b/sdk/cosmosdb/cosmos/src/queryExecutionContext/pipelinedQueryExecutionContext.ts index 7aab04d96a38..181c48cc8bcc 100644 --- a/sdk/cosmosdb/cosmos/src/queryExecutionContext/pipelinedQueryExecutionContext.ts +++ b/sdk/cosmosdb/cosmos/src/queryExecutionContext/pipelinedQueryExecutionContext.ts @@ -14,6 +14,7 @@ import { getInitialHeader, mergeHeaders } from "./headerUtils"; import { OrderByQueryExecutionContext } from "./orderByQueryExecutionContext"; import { ParallelQueryExecutionContext } from "./parallelQueryExecutionContext"; import { GroupByValueEndpointComponent } from "./EndpointComponent/GroupByValueEndpointComponent"; +import { SqlQuerySpec } from "./SqlQuerySpec"; /** @hidden */ export class PipelinedQueryExecutionContext implements ExecutionContext { @@ -25,7 +26,7 @@ export class PipelinedQueryExecutionContext implements ExecutionContext { constructor( private clientContext: ClientContext, private collectionLink: string, - private query: any, // TODO: any query + private query: string | SqlQuerySpec, private options: FeedOptions, private partitionedQueryExecutionInfo: PartitionedQueryExecutionInfo ) { diff --git a/sdk/cosmosdb/cosmos/src/queryIterator.ts b/sdk/cosmosdb/cosmos/src/queryIterator.ts index d668b7747cdd..9382d84261eb 100644 --- a/sdk/cosmosdb/cosmos/src/queryIterator.ts +++ b/sdk/cosmosdb/cosmos/src/queryIterator.ts @@ -85,8 +85,8 @@ export class QueryIterator { await this.createPipelinedExecutionContext(); try { response = await this.queryExecutionContext.fetchMore(); - } catch (error) { - this.handleSplitError(error); + } catch (queryError) { + this.handleSplitError(queryError); } } else { throw error; @@ -149,8 +149,8 @@ export class QueryIterator { await this.createPipelinedExecutionContext(); try { response = await this.queryExecutionContext.fetchMore(); - } catch (error) { - this.handleSplitError(error); + } catch (queryError) { + this.handleSplitError(queryError); } } else { throw error; @@ -166,7 +166,7 @@ export class QueryIterator { /** * Reset the QueryIterator to the beginning and clear all the resources inside it */ - public reset() { + public reset(): void { this.queryPlanPromise = undefined; this.queryExecutionContext = new DefaultQueryExecutionContext( this.options, @@ -206,7 +206,7 @@ export class QueryIterator { ); } - private async createPipelinedExecutionContext() { + private async createPipelinedExecutionContext(): Promise { const queryPlanResponse = await this.queryPlanPromise; // We always coerce queryPlanPromise to resolved. So if it errored, we need to manually inspect the resolved value @@ -228,7 +228,7 @@ export class QueryIterator { ); } - private async fetchQueryPlan() { + private async fetchQueryPlan(): Promise { if (!this.queryPlanPromise && this.resourceType === ResourceType.item) { return this.clientContext .getQueryPlan( @@ -255,7 +255,7 @@ export class QueryIterator { } private initPromise: Promise; - private async init() { + private async init(): Promise { if (this.isInitialized === true) { return; } @@ -264,14 +264,14 @@ export class QueryIterator { } return this.initPromise; } - private async _init() { + private async _init(): Promise { if (this.options.forceQueryPlan === true && this.resourceType === ResourceType.item) { await this.createPipelinedExecutionContext(); } this.isInitialized = true; } - private handleSplitError(err: any) { + private handleSplitError(err: any): void { if (err.code === 410) { const error = new Error( "Encountered partition split and could not recover. This request is retryable" diff --git a/sdk/cosmosdb/cosmos/src/queryMetrics/clientSideMetrics.ts b/sdk/cosmosdb/cosmos/src/queryMetrics/clientSideMetrics.ts index ebe2e714f678..090766a9d97b 100644 --- a/sdk/cosmosdb/cosmos/src/queryMetrics/clientSideMetrics.ts +++ b/sdk/cosmosdb/cosmos/src/queryMetrics/clientSideMetrics.ts @@ -7,7 +7,7 @@ export class ClientSideMetrics { /** * Adds one or more ClientSideMetrics to a copy of this instance and returns the result. */ - public add(...clientSideMetricsArray: ClientSideMetrics[]) { + public add(...clientSideMetricsArray: ClientSideMetrics[]): ClientSideMetrics { let requestCharge = this.requestCharge; for (const clientSideMetrics of clientSideMetricsArray) { if (clientSideMetrics == null) { @@ -22,7 +22,7 @@ export class ClientSideMetrics { public static readonly zero = new ClientSideMetrics(0); - public static createFromArray(...clientSideMetricsArray: ClientSideMetrics[]) { + public static createFromArray(...clientSideMetricsArray: ClientSideMetrics[]): ClientSideMetrics { if (clientSideMetricsArray == null) { throw new Error("clientSideMetricsArray is null or undefined item(s)"); } diff --git a/sdk/cosmosdb/cosmos/src/queryMetrics/queryMetrics.ts b/sdk/cosmosdb/cosmos/src/queryMetrics/queryMetrics.ts index 40082f3b7206..892fabc6f844 100644 --- a/sdk/cosmosdb/cosmos/src/queryMetrics/queryMetrics.ts +++ b/sdk/cosmosdb/cosmos/src/queryMetrics/queryMetrics.ts @@ -28,7 +28,7 @@ export class QueryMetrics { * Gets the IndexHitRatio * @hidden */ - public get indexHitRatio() { + public get indexHitRatio(): number { return this.retrievedDocumentCount === 0 ? 1 : this.indexHitDocumentCount / this.retrievedDocumentCount; @@ -37,7 +37,7 @@ export class QueryMetrics { /** * returns a new QueryMetrics instance that is the addition of this and the arguments. */ - public add(queryMetricsArray: QueryMetrics[]) { + public add(queryMetricsArray: QueryMetrics[]): QueryMetrics { let retrievedDocumentCount = 0; let retrievedDocumentSize = 0; let outputDocumentCount = 0; @@ -93,7 +93,7 @@ export class QueryMetrics { * Output the QueryMetrics as a delimited string. * @hidden */ - public toDelimitedString() { + public toDelimitedString(): string { return ( QueryMetricsConstants.RetrievedDocumentCount + "=" + @@ -160,7 +160,7 @@ export class QueryMetrics { /** * Returns a new instance of the QueryMetrics class that is the aggregation of an array of query metrics. */ - public static createFromArray(queryMetricsArray: QueryMetrics[]) { + public static createFromArray(queryMetricsArray: QueryMetrics[]): QueryMetrics { if (!queryMetricsArray) { throw new Error("queryMetricsArray is null or undefined item(s)"); } @@ -174,7 +174,7 @@ export class QueryMetrics { public static createFromDelimitedString( delimitedString: string, clientSideMetrics?: ClientSideMetrics - ) { + ): QueryMetrics { const metrics = parseDelimitedString(delimitedString); const indexHitRatio = metrics[QueryMetricsConstants.IndexHitRatio] || 0; diff --git a/sdk/cosmosdb/cosmos/src/queryMetrics/queryMetricsUtils.ts b/sdk/cosmosdb/cosmos/src/queryMetrics/queryMetricsUtils.ts index 431244aba9b9..2153b943b69f 100644 --- a/sdk/cosmosdb/cosmos/src/queryMetrics/queryMetricsUtils.ts +++ b/sdk/cosmosdb/cosmos/src/queryMetrics/queryMetricsUtils.ts @@ -5,7 +5,11 @@ import { TimeSpan } from "./timeSpan"; /** * @hidden */ -export function parseDelimitedString(delimitedString: string) { +export function parseDelimitedString( + delimitedString: string +): { + [key: string]: any; +} { if (delimitedString == null) { throw new Error("delimitedString is null or undefined"); } @@ -32,7 +36,10 @@ export function parseDelimitedString(delimitedString: string) { /** * @hidden */ -export function timeSpanFromMetrics(metrics: { [key: string]: any } /* TODO: any */, key: string) { +export function timeSpanFromMetrics( + metrics: { [key: string]: any } /* TODO: any */, + key: string +): TimeSpan { if (key in metrics) { return TimeSpan.fromMilliseconds(metrics[key]); } @@ -43,6 +50,6 @@ export function timeSpanFromMetrics(metrics: { [key: string]: any } /* TODO: any /** * @hidden */ -export function isNumeric(input: any) { - return !isNaN(parseFloat(input)) && isFinite(input); +export function isNumeric(input: unknown): boolean { + return !isNaN(parseFloat(input as string)) && isFinite(input as number); } diff --git a/sdk/cosmosdb/cosmos/src/queryMetrics/queryPreparationTime.ts b/sdk/cosmosdb/cosmos/src/queryMetrics/queryPreparationTime.ts index 8e3447dcc3f7..e529c615bfc1 100644 --- a/sdk/cosmosdb/cosmos/src/queryMetrics/queryPreparationTime.ts +++ b/sdk/cosmosdb/cosmos/src/queryMetrics/queryPreparationTime.ts @@ -15,7 +15,7 @@ export class QueryPreparationTimes { /** * returns a new QueryPreparationTimes instance that is the addition of this and the arguments. */ - public add(...queryPreparationTimesArray: QueryPreparationTimes[]) { + public add(...queryPreparationTimesArray: QueryPreparationTimes[]): QueryPreparationTimes { let queryCompilationTime = this.queryCompilationTime; let logicalPlanBuildTime = this.logicalPlanBuildTime; let physicalPlanBuildTime = this.physicalPlanBuildTime; @@ -47,7 +47,7 @@ export class QueryPreparationTimes { /** * Output the QueryPreparationTimes as a delimited string. */ - public toDelimitedString() { + public toDelimitedString(): string { return ( `${ QueryMetricsConstants.QueryCompileTimeInMs @@ -75,7 +75,9 @@ export class QueryPreparationTimes { * Returns a new instance of the QueryPreparationTimes class that is the * aggregation of an array of QueryPreparationTimes. */ - public static createFromArray(queryPreparationTimesArray: QueryPreparationTimes[]) { + public static createFromArray( + queryPreparationTimesArray: QueryPreparationTimes[] + ): QueryPreparationTimes { if (queryPreparationTimesArray == null) { throw new Error("queryPreparationTimesArray is null or undefined item(s)"); } @@ -86,7 +88,7 @@ export class QueryPreparationTimes { /** * Returns a new instance of the QueryPreparationTimes class this is deserialized from a delimited string. */ - public static createFromDelimitedString(delimitedString: string) { + public static createFromDelimitedString(delimitedString: string): QueryPreparationTimes { const metrics = parseDelimitedString(delimitedString); return new QueryPreparationTimes( diff --git a/sdk/cosmosdb/cosmos/src/queryMetrics/runtimeExecutionTimes.ts b/sdk/cosmosdb/cosmos/src/queryMetrics/runtimeExecutionTimes.ts index 9b56dce9bd3f..b77dbb056f00 100644 --- a/sdk/cosmosdb/cosmos/src/queryMetrics/runtimeExecutionTimes.ts +++ b/sdk/cosmosdb/cosmos/src/queryMetrics/runtimeExecutionTimes.ts @@ -14,7 +14,7 @@ export class RuntimeExecutionTimes { /** * returns a new RuntimeExecutionTimes instance that is the addition of this and the arguments. */ - public add(...runtimeExecutionTimesArray: RuntimeExecutionTimes[]) { + public add(...runtimeExecutionTimesArray: RuntimeExecutionTimes[]): RuntimeExecutionTimes { let queryEngineExecutionTime = this.queryEngineExecutionTime; let systemFunctionExecutionTime = this.systemFunctionExecutionTime; let userDefinedFunctionExecutionTime = this.userDefinedFunctionExecutionTime; @@ -45,7 +45,7 @@ export class RuntimeExecutionTimes { /** * Output the RuntimeExecutionTimes as a delimited string. */ - public toDelimitedString() { + public toDelimitedString(): string { // tslint:disable-next-line:max-line-length return ( `${ @@ -68,7 +68,9 @@ export class RuntimeExecutionTimes { * Returns a new instance of the RuntimeExecutionTimes class that is * the aggregation of an array of RuntimeExecutionTimes. */ - public static createFromArray(runtimeExecutionTimesArray: RuntimeExecutionTimes[]) { + public static createFromArray( + runtimeExecutionTimesArray: RuntimeExecutionTimes[] + ): RuntimeExecutionTimes { if (runtimeExecutionTimesArray == null) { throw new Error("runtimeExecutionTimesArray is null or undefined item(s)"); } @@ -79,7 +81,7 @@ export class RuntimeExecutionTimes { /** * Returns a new instance of the RuntimeExecutionTimes class this is deserialized from a delimited string. */ - public static createFromDelimitedString(delimitedString: string) { + public static createFromDelimitedString(delimitedString: string): RuntimeExecutionTimes { const metrics = parseDelimitedString(delimitedString); const vmExecutionTime = timeSpanFromMetrics(metrics, QueryMetricsConstants.VMExecutionTimeInMs); diff --git a/sdk/cosmosdb/cosmos/src/queryMetrics/timeSpan.ts b/sdk/cosmosdb/cosmos/src/queryMetrics/timeSpan.ts index 1bf9828f9958..e53b01b0f3eb 100644 --- a/sdk/cosmosdb/cosmos/src/queryMetrics/timeSpan.ts +++ b/sdk/cosmosdb/cosmos/src/queryMetrics/timeSpan.ts @@ -92,7 +92,7 @@ export class TimeSpan { * Returns a new TimeSpan object whose value is the sum of the specified TimeSpan object and this instance. * @param ts - The time interval to add. */ - public add(ts: TimeSpan) { + public add(ts: TimeSpan): TimeSpan { if (TimeSpan.additionDoesOverflow(this._ticks, ts._ticks)) { throw new Error("Adding the two timestamps causes an overflow."); } @@ -105,7 +105,7 @@ export class TimeSpan { * Returns a new TimeSpan object whose value is the difference of the specified TimeSpan object and this instance. * @param ts - The time interval to subtract. */ - public subtract(ts: TimeSpan) { + public subtract(ts: TimeSpan): TimeSpan { if (TimeSpan.subtractionDoesUnderflow(this._ticks, ts._ticks)) { throw new Error("Subtracting the two timestamps causes an underflow."); } @@ -119,7 +119,7 @@ export class TimeSpan { * instance is shorter than, equal to, or longer than the specified object. * @param value - The time interval to add. */ - public compareTo(value: TimeSpan) { + public compareTo(value: TimeSpan): 1 | -1 | 0 { if (value == null) { return 1; } @@ -134,7 +134,7 @@ export class TimeSpan { /** * Returns a new TimeSpan object whose value is the absolute value of the current TimeSpan object. */ - public duration() { + public duration(): TimeSpan { return TimeSpan.fromTicks(this._ticks >= 0 ? this._ticks : -this._ticks); } @@ -142,7 +142,7 @@ export class TimeSpan { * Returns a value indicating whether this instance is equal to a specified object. * @param value - The time interval to check for equality. */ - public equals(value: TimeSpan) { + public equals(value: TimeSpan): boolean { if (TimeSpan.isTimeSpan(value)) { return this._ticks === value._ticks; } @@ -154,50 +154,50 @@ export class TimeSpan { * Returns a new TimeSpan object whose value is the negated value of this instance. * @param value - The time interval to check for equality. */ - public negate() { + public negate(): TimeSpan { return TimeSpan.fromTicks(-this._ticks); } - public days() { + public days(): number { return Math.floor(this._ticks / ticksPerDay); } - public hours() { + public hours(): number { return Math.floor(this._ticks / ticksPerHour); } - public milliseconds() { + public milliseconds(): number { return Math.floor(this._ticks / ticksPerMillisecond); } - public seconds() { + public seconds(): number { return Math.floor(this._ticks / ticksPerSecond); } - public ticks() { + public ticks(): number { return this._ticks; } - public totalDays() { + public totalDays(): number { return this._ticks * daysPerTick; } - public totalHours() { + public totalHours(): number { return this._ticks * hoursPerTick; } - public totalMilliseconds() { + public totalMilliseconds(): number { return this._ticks * millisecondsPerTick; } - public totalMinutes() { + public totalMinutes(): number { return this._ticks * minutesPerTick; } - public totalSeconds() { + public totalSeconds(): number { return this._ticks * secondsPerTick; } - public static fromTicks(value: number) { + public static fromTicks(value: number): TimeSpan { const timeSpan = new TimeSpan(0, 0, 0, 0, 0); timeSpan._ticks = value; return timeSpan; @@ -207,21 +207,21 @@ export class TimeSpan { public static readonly maxValue = TimeSpan.fromTicks(Number.MAX_SAFE_INTEGER); public static readonly minValue = TimeSpan.fromTicks(Number.MIN_SAFE_INTEGER); - public static isTimeSpan(timespan: TimeSpan) { + public static isTimeSpan(timespan: TimeSpan): number { return timespan._ticks; } - public static additionDoesOverflow(a: number, b: number) { + public static additionDoesOverflow(a: number, b: number): boolean { const c = a + b; return a !== c - b || b !== c - a; } - public static subtractionDoesUnderflow(a: number, b: number) { + public static subtractionDoesUnderflow(a: number, b: number): boolean { const c = a - b; return a !== c + b || b !== a - c; } - public static compare(t1: TimeSpan, t2: TimeSpan) { + public static compare(t1: TimeSpan, t2: TimeSpan): 1 | 0 | -1 { if (t1._ticks > t2._ticks) { return 1; } @@ -231,7 +231,7 @@ export class TimeSpan { return 0; } - public static interval(value: number, scale: number) { + public static interval(value: number, scale: number): TimeSpan { if (isNaN(value)) { throw new Error("value must be a number"); } @@ -244,23 +244,23 @@ export class TimeSpan { return TimeSpan.fromTicks(Math.floor(milliseconds * ticksPerMillisecond)); } - public static fromMilliseconds(value: number) { + public static fromMilliseconds(value: number): TimeSpan { return TimeSpan.interval(value, 1); } - public static fromSeconds(value: number) { + public static fromSeconds(value: number): TimeSpan { return TimeSpan.interval(value, millisPerSecond); } - public static fromMinutes(value: number) { + public static fromMinutes(value: number): TimeSpan { return TimeSpan.interval(value, millisPerMinute); } - public static fromHours(value: number) { + public static fromHours(value: number): TimeSpan { return TimeSpan.interval(value, millisPerHour); } - public static fromDays(value: number) { + public static fromDays(value: number): TimeSpan { return TimeSpan.interval(value, millisPerDay); } } diff --git a/sdk/cosmosdb/cosmos/src/request/RequestHandler.ts b/sdk/cosmosdb/cosmos/src/request/RequestHandler.ts index d11915e2514b..7266e6f07d19 100644 --- a/sdk/cosmosdb/cosmos/src/request/RequestHandler.ts +++ b/sdk/cosmosdb/cosmos/src/request/RequestHandler.ts @@ -17,14 +17,21 @@ import { TimeoutError } from "./TimeoutError"; /** @hidden */ const log = logger("RequestHandler"); -async function executeRequest(requestContext: RequestContext) { +async function executeRequest(requestContext: RequestContext): Promise> { return executePlugins(requestContext, httpRequest, PluginOn.request); } /** * @hidden */ -async function httpRequest(requestContext: RequestContext) { +async function httpRequest( + requestContext: RequestContext +): Promise<{ + headers: any; + result: any; + code: number; + substatus: number; +}> { const controller = new AbortController(); const signal = controller.signal; diff --git a/sdk/cosmosdb/cosmos/src/request/StatusCodes.ts b/sdk/cosmosdb/cosmos/src/request/StatusCodes.ts index 3d7ceed77e82..5ba9083be999 100644 --- a/sdk/cosmosdb/cosmos/src/request/StatusCodes.ts +++ b/sdk/cosmosdb/cosmos/src/request/StatusCodes.ts @@ -1,61 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -/** - * @hidden - */ -export const StatusCode = { - // Success - Ok: 200 as 200, - Created: 201 as 201, - Accepted: 202 as 202, - NoContent: 204 as 204, - NotModified: 304 as 304, - - // Client error - BadRequest: 400 as 400, - Unauthorized: 401 as 401, - Forbidden: 403 as 403, - NotFound: 404 as 404, - MethodNotAllowed: 405 as 405, - RequestTimeout: 408 as 408, - Conflict: 409 as 409, - Gone: 410 as 410, - PreconditionFailed: 412 as 412, - RequestEntityTooLarge: 413 as 413, - TooManyRequests: 429 as 429, - RetryWith: 449 as 449, - - // Server Error - InternalServerError: 500 as 500, - ServiceUnavailable: 503 as 503, - - // Operation pause and cancel. These are FAKE status codes for QOS logging purpose only. - OperationPaused: 1200 as 1200, - OperationCancelled: 1201 -}; /** * @hidden */ -export type StatusCode = typeof StatusCode[keyof typeof StatusCode]; - +export type StatusCode = number; /** * @hidden */ -export const SubStatusCode = { - Unknown: 0 as 0, - - // 400: Bad Request Substatus - CrossPartitionQueryNotServable: 1004 as 1004, - - // 410: StatusCodeType_Gone: substatus - PartitionKeyRangeGone: 1002 as 1002, - - // 404: NotFound Substatus - ReadSessionNotAvailable: 1002 as 1002, - - // 403: Forbidden Substatus - WriteForbidden: 3 -}; - -export type SubStatusCode = typeof SubStatusCode[keyof typeof SubStatusCode]; +export type SubStatusCode = number; diff --git a/sdk/cosmosdb/cosmos/src/request/defaultAgent.ts b/sdk/cosmosdb/cosmos/src/request/defaultAgent.ts index c9224e652222..bb3cad366876 100644 --- a/sdk/cosmosdb/cosmos/src/request/defaultAgent.ts +++ b/sdk/cosmosdb/cosmos/src/request/defaultAgent.ts @@ -2,19 +2,15 @@ // Licensed under the MIT license. import { Agent } from "http"; -/** - * @hidden - */ -export let defaultHttpAgent: Agent; /** * @hidden */ export let defaultHttpsAgent: Agent; // tslint:disable-next-line:no-var-requires -const https = require("https"); +const https = require("https"); // eslint-disable-line @typescript-eslint/no-require-imports // tslint:disable-next-line:no-var-requires -const tls = require("tls"); +const tls = require("tls"); // eslint-disable-line @typescript-eslint/no-require-imports // minVersion only available in Node 10+ if (tls.DEFAULT_MIN_VERSION) { @@ -30,7 +26,10 @@ if (tls.DEFAULT_MIN_VERSION) { }); } // tslint:disable-next-line:no-var-requires -const http = require("http"); -defaultHttpAgent = new http.Agent({ +const http = require("http"); // eslint-disable-line @typescript-eslint/no-require-imports +/** + * @internal + */ +export const defaultHttpAgent: Agent = new http.Agent({ keepAlive: true }); diff --git a/sdk/cosmosdb/cosmos/src/request/request.ts b/sdk/cosmosdb/cosmos/src/request/request.ts index 6e5bac79f93a..305be4a5cbcf 100644 --- a/sdk/cosmosdb/cosmos/src/request/request.ts +++ b/sdk/cosmosdb/cosmos/src/request/request.ts @@ -12,7 +12,7 @@ import { FeedOptions, RequestOptions } from "./index"; // /** @hidden */ -function javaScriptFriendlyJSONStringify(s: object) { +function javaScriptFriendlyJSONStringify(s: unknown): string { // two line terminators (Line separator and Paragraph separator) are not needed to be escaped in JSON // but are needed to be escaped in JavaScript. return JSON.stringify(s) @@ -21,7 +21,7 @@ function javaScriptFriendlyJSONStringify(s: object) { } /** @hidden */ -export function bodyFromData(data: Buffer | string | object) { +export function bodyFromData(data: Buffer | string | Record): string { if (typeof data === "object") { return javaScriptFriendlyJSONStringify(data); } diff --git a/sdk/cosmosdb/cosmos/src/retry/defaultRetryPolicy.ts b/sdk/cosmosdb/cosmos/src/retry/defaultRetryPolicy.ts index 76d643080217..0c27f716f9de 100644 --- a/sdk/cosmosdb/cosmos/src/retry/defaultRetryPolicy.ts +++ b/sdk/cosmosdb/cosmos/src/retry/defaultRetryPolicy.ts @@ -109,7 +109,7 @@ const CONNECTION_ERROR_CODES = [ /** * @hidden */ -function needsRetry(operationType: OperationType, code: number | string) { +function needsRetry(operationType: OperationType, code: number | string): boolean { if ( (operationType === OperationType.Read || operationType === OperationType.Query) && CONNECTION_ERROR_CODES.indexOf(code) !== -1 diff --git a/sdk/cosmosdb/cosmos/src/routing/CollectionRoutingMapFactory.ts b/sdk/cosmosdb/cosmos/src/routing/CollectionRoutingMapFactory.ts index bf345ff74dba..1762f641ccaa 100644 --- a/sdk/cosmosdb/cosmos/src/routing/CollectionRoutingMapFactory.ts +++ b/sdk/cosmosdb/cosmos/src/routing/CollectionRoutingMapFactory.ts @@ -6,7 +6,7 @@ import { InMemoryCollectionRoutingMap } from "./inMemoryCollectionRoutingMap"; /** * @hidden */ -function compareRanges(a: any, b: any) { +function compareRanges(a: any, b: any): 0 | 1 | -1 { const aVal = a[0][Constants.PartitionKeyRange.MinInclusive]; const bVal = b[0][Constants.PartitionKeyRange.MinInclusive]; if (aVal > bVal) { @@ -19,7 +19,9 @@ function compareRanges(a: any, b: any) { } /** @hidden */ -export function createCompleteRoutingMap(partitionKeyRangeInfoTuppleList: any[]) { +export function createCompleteRoutingMap( + partitionKeyRangeInfoTuppleList: any[] +): InMemoryCollectionRoutingMap { const rangeById: any = {}; // TODO: any const rangeByInfo: any = {}; // TODO: any @@ -45,7 +47,7 @@ export function createCompleteRoutingMap(partitionKeyRangeInfoTuppleList: any[]) /** * @hidden */ -function isCompleteSetOfRange(partitionKeyOrderedRange: any) { +function isCompleteSetOfRange(partitionKeyOrderedRange: any): boolean { // TODO: any let isComplete = false; if (partitionKeyOrderedRange.length > 0) { diff --git a/sdk/cosmosdb/cosmos/src/routing/QueryRange.ts b/sdk/cosmosdb/cosmos/src/routing/QueryRange.ts index ed4220dbebf8..923ad00875b7 100644 --- a/sdk/cosmosdb/cosmos/src/routing/QueryRange.ts +++ b/sdk/cosmosdb/cosmos/src/routing/QueryRange.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import { PartitionKeyRange } from "../client/Container/PartitionKeyRange"; import { Constants } from "../common"; +import { QueryRange as ResponseQueryRange } from "../request/ErrorResponse"; /** @hidden */ export class QueryRange { @@ -29,9 +31,9 @@ export class QueryRange { this.isMinInclusive = isMinInclusive; this.isMaxInclusive = isMaxInclusive; } - public overlaps(other: QueryRange) { + public overlaps(other: QueryRange): boolean { // tslint:disable-next-line:no-this-assignment - const range1 = this; + const range1 = this; // eslint-disable-line @typescript-eslint/no-this-alias const range2 = other; if (range1 === undefined || range2 === undefined) { return false; @@ -52,7 +54,7 @@ export class QueryRange { return false; } - public isFullRange() { + public isFullRange(): boolean { return ( this.min === Constants.EffectiveParitionKeyConstants.MinimumInclusiveEffectivePartitionKey && this.max === Constants.EffectiveParitionKeyConstants.MaximumExclusiveEffectivePartitionKey && @@ -61,7 +63,7 @@ export class QueryRange { ); } - public isEmpty() { + public isEmpty(): boolean { return !(this.isMinInclusive && this.isMaxInclusive) && this.min === this.max; } /** @@ -69,8 +71,7 @@ export class QueryRange { * @returns QueryRange * @hidden */ - public static parsePartitionKeyRange(partitionKeyRange: any) { - // TODO: paritionkeyrange + public static parsePartitionKeyRange(partitionKeyRange: PartitionKeyRange): QueryRange { return new QueryRange( partitionKeyRange[Constants.PartitionKeyRange.MinInclusive], partitionKeyRange[Constants.PartitionKeyRange.MaxExclusive], @@ -83,8 +84,7 @@ export class QueryRange { * @returns QueryRange * @hidden */ - public static parseFromDict(queryRangeDict: any) { - // TODO: queryRangeDictionary + public static parseFromDict(queryRangeDict: ResponseQueryRange): QueryRange { return new QueryRange( queryRangeDict.min, queryRangeDict.max, diff --git a/sdk/cosmosdb/cosmos/src/routing/inMemoryCollectionRoutingMap.ts b/sdk/cosmosdb/cosmos/src/routing/inMemoryCollectionRoutingMap.ts index 7938c240e06d..8d3f5270575c 100644 --- a/sdk/cosmosdb/cosmos/src/routing/inMemoryCollectionRoutingMap.ts +++ b/sdk/cosmosdb/cosmos/src/routing/inMemoryCollectionRoutingMap.ts @@ -1,21 +1,22 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import { PartitionKeyRange } from "../client"; import { Constants } from "../common"; import { QueryRange } from "./QueryRange"; /** @hidden */ export class InMemoryCollectionRoutingMap { - private orderedPartitionKeyRanges: any[]; + private orderedPartitionKeyRanges: PartitionKeyRange[]; private orderedRanges: QueryRange[]; // TODO: chrande made this public, even though it is implementation detail for a test - public orderedPartitionInfo: any; + public orderedPartitionInfo: unknown; /** * Represents a InMemoryCollectionRoutingMap Object, * Stores partition key ranges in an efficient way with some additional information and provides * convenience methods for working with set of ranges. */ - constructor(orderedPartitionKeyRanges: any[], orderedPartitionInfo: any) { + constructor(orderedPartitionKeyRanges: PartitionKeyRange[], orderedPartitionInfo: unknown) { this.orderedPartitionKeyRanges = orderedPartitionKeyRanges; this.orderedRanges = orderedPartitionKeyRanges.map((pkr) => { return new QueryRange( @@ -27,11 +28,11 @@ export class InMemoryCollectionRoutingMap { }); this.orderedPartitionInfo = orderedPartitionInfo; } - public getOrderedParitionKeyRanges() { + public getOrderedParitionKeyRanges(): PartitionKeyRange[] { return this.orderedPartitionKeyRanges; } - public getOverlappingRanges(providedQueryRanges: QueryRange | QueryRange[]) { + public getOverlappingRanges(providedQueryRanges: QueryRange | QueryRange[]): PartitionKeyRange[] { // TODO This code has all kinds of smells. Multiple iterations and sorts just to grab overlapping ranges // stfaul attempted to bring it down to one for-loop and failed const pqr: QueryRange[] = Array.isArray(providedQueryRanges) diff --git a/sdk/cosmosdb/cosmos/src/routing/partitionKeyRangeCache.ts b/sdk/cosmosdb/cosmos/src/routing/partitionKeyRangeCache.ts index a9d1160f94fb..3c73f8c6726b 100644 --- a/sdk/cosmosdb/cosmos/src/routing/partitionKeyRangeCache.ts +++ b/sdk/cosmosdb/cosmos/src/routing/partitionKeyRangeCache.ts @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import { PartitionKeyRange } from "../client/Container/PartitionKeyRange"; import { ClientContext } from "../ClientContext"; import { getIdFromLink } from "../common/helper"; import { createCompleteRoutingMap } from "./CollectionRoutingMapFactory"; @@ -36,12 +37,17 @@ export class PartitionKeyRangeCache { * Given the query ranges and a collection, invokes the callback on the list of overlapping partition key ranges * @hidden */ - public async getOverlappingRanges(collectionLink: string, queryRange: QueryRange) { + public async getOverlappingRanges( + collectionLink: string, + queryRange: QueryRange + ): Promise { const crm = await this.onCollectionRoutingMap(collectionLink); return crm.getOverlappingRanges(queryRange); } - private async requestCollectionRoutingMap(collectionLink: string) { + private async requestCollectionRoutingMap( + collectionLink: string + ): Promise { const { resources } = await this.clientContext .queryPartitionKeyRanges(collectionLink) .fetchAll(); diff --git a/sdk/cosmosdb/cosmos/src/routing/smartRoutingMapProvider.ts b/sdk/cosmosdb/cosmos/src/routing/smartRoutingMapProvider.ts index fd9ce2778972..ab992cac4a8a 100644 --- a/sdk/cosmosdb/cosmos/src/routing/smartRoutingMapProvider.ts +++ b/sdk/cosmosdb/cosmos/src/routing/smartRoutingMapProvider.ts @@ -15,7 +15,7 @@ export class SmartRoutingMapProvider { constructor(clientContext: ClientContext) { this.partitionKeyRangeCache = new PartitionKeyRangeCache(clientContext); } - private static _secondRangeIsAfterFirstRange(range1: QueryRange, range2: QueryRange) { + private static _secondRangeIsAfterFirstRange(range1: QueryRange, range2: QueryRange): boolean { if (typeof range1.max === "undefined") { throw new Error("range1 must have max"); } @@ -37,7 +37,7 @@ export class SmartRoutingMapProvider { } } - private static _isSortedAndNonOverlapping(ranges: QueryRange[]) { + private static _isSortedAndNonOverlapping(ranges: QueryRange[]): boolean { for (let idx = 1; idx < ranges.length; idx++) { const previousR = ranges[idx - 1]; const r = ranges[idx]; @@ -48,15 +48,15 @@ export class SmartRoutingMapProvider { return true; } - private static _stringMax(a: string, b: string) { + private static _stringMax(a: string, b: string): string { return a >= b ? a : b; } - private static _stringCompare(a: string, b: string) { + private static _stringCompare(a: string, b: string): 1 | 0 | -1 { return a === b ? 0 : a > b ? 1 : -1; } - private static _subtractRange(r: QueryRange, partitionKeyRange: any) { + private static _subtractRange(r: QueryRange, partitionKeyRange: any): QueryRange { const left = this._stringMax(partitionKeyRange[PARITIONKEYRANGE.MaxExclusive], r.min); const leftInclusive = this._stringCompare(left, r.min) === 0 ? r.isMinInclusive : false; return new QueryRange(left, r.max, leftInclusive, r.isMaxInclusive); @@ -89,7 +89,7 @@ export class SmartRoutingMapProvider { let index = 0; let currentProvidedRange = sortedRanges[index]; - while (true) { + for (;;) { if (currentProvidedRange.isEmpty()) { // skip and go to the next item if (++index >= sortedRanges.length) { diff --git a/sdk/cosmosdb/cosmos/src/session/VectorSessionToken.ts b/sdk/cosmosdb/cosmos/src/session/VectorSessionToken.ts index fbe00246f842..ea8f638b0262 100644 --- a/sdk/cosmosdb/cosmos/src/session/VectorSessionToken.ts +++ b/sdk/cosmosdb/cosmos/src/session/VectorSessionToken.ts @@ -124,7 +124,7 @@ export class VectorSessionToken { ); } - public toString() { + public toString(): string | undefined { return this.sessionToken; } @@ -147,7 +147,7 @@ export class VectorSessionToken { /** * @hidden */ -function max(int1: string, int2: string) { +function max(int1: string, int2: string): string { // NOTE: This only works for positive numbers if (int1.length === int2.length) { return int1 > int2 ? int1 : int2; diff --git a/sdk/cosmosdb/cosmos/src/session/sessionContainer.ts b/sdk/cosmosdb/cosmos/src/session/sessionContainer.ts index 3a89af16585e..d34d233e44fa 100644 --- a/sdk/cosmosdb/cosmos/src/session/sessionContainer.ts +++ b/sdk/cosmosdb/cosmos/src/session/sessionContainer.ts @@ -16,7 +16,7 @@ export class SessionContainer { private collectionResourceIdToSessionTokens = new Map>() ) {} - public get(request: SessionContext) { + public get(request: SessionContext): string { if (!request) { throw new Error("request cannot be null"); } @@ -25,7 +25,7 @@ export class SessionContainer { return SessionContainer.getCombinedSessionTokenString(rangeIdToTokenMap); } - public remove(request: SessionContext) { + public remove(request: SessionContext): void { let collectionResourceId: string; const resourceAddress = trimSlashes(request.resourceAddress); const collectionName = getContainerLink(resourceAddress); @@ -38,7 +38,7 @@ export class SessionContainer { } } - public set(request: SessionContext, resHeaders: CosmosHeaders) { + public set(request: SessionContext, resHeaders: CosmosHeaders): void { // TODO: we check the master logic a few different places. Might not need it. if ( !resHeaders || @@ -76,7 +76,7 @@ export class SessionContainer { } } - private validateOwnerID(ownerId: string) { + private validateOwnerID(ownerId: string): boolean { // If ownerId contains exactly 8 bytes it represents a unique database+collection identifier. Otherwise it represents another resource // The first 4 bytes are the database. The last 4 bytes are the collection. // Cosmos rids potentially contain "-" which is an invalid character in the browser atob implementation @@ -97,7 +97,7 @@ export class SessionContainer { return rangeIdToTokenMap; } - private static getCombinedSessionTokenString(tokens: Map) { + private static getCombinedSessionTokenString(tokens: Map): string { if (!tokens || tokens.size === 0) { return SessionContainer.EMPTY_SESSION_TOKEN; } @@ -116,7 +116,7 @@ export class SessionContainer { private static compareAndSetToken( newTokenString: string, containerSessionTokens: Map - ) { + ): void { if (!newTokenString) { return; } @@ -159,7 +159,7 @@ export class SessionContainer { return false; } - private getContainerName(request: SessionContext, headers: CosmosHeaders) { + private getContainerName(request: SessionContext, headers: CosmosHeaders): string { let ownerFullName = headers[Constants.HttpHeaders.OwnerFullName]; if (!ownerFullName) { ownerFullName = trimSlashes(request.resourceAddress); diff --git a/sdk/cosmosdb/cosmos/src/utils/atob.browser.ts b/sdk/cosmosdb/cosmos/src/utils/atob.browser.ts index 286f50149d60..93c5917396f5 100644 --- a/sdk/cosmosdb/cosmos/src/utils/atob.browser.ts +++ b/sdk/cosmosdb/cosmos/src/utils/atob.browser.ts @@ -6,11 +6,11 @@ let safeatob: any; // base64 character set, plus padding character (=) const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; // Regular expression to check formal correctness of base64 encoded strings -const b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/; +const b64re = /^(?:[A-Za-z\d+/]{4})*?(?:[A-Za-z\d+/]{2}(?:==)?|[A-Za-z\d+/]{3}=?)?$/; if ("function" !== typeof atob) { // atob implementation for React Native - safeatob = (str: string) => { + safeatob = (str: string): string => { // atob can work with strings with whitespaces, even inside the encoded part, // but only \t, \n, \f, \r and ' ', which can be stripped. str = String(str).replace(/[\t\n\f\r ]+/g, ""); diff --git a/sdk/cosmosdb/cosmos/src/utils/atob.ts b/sdk/cosmosdb/cosmos/src/utils/atob.ts index 2239926f9ff7..e05ba43ac6a8 100644 --- a/sdk/cosmosdb/cosmos/src/utils/atob.ts +++ b/sdk/cosmosdb/cosmos/src/utils/atob.ts @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -export default function atob(str: string) { +export default function atob(str: string): string { return Buffer.from(str, "base64").toString("binary"); } diff --git a/sdk/cosmosdb/cosmos/src/utils/batch.ts b/sdk/cosmosdb/cosmos/src/utils/batch.ts index c0c52feaed2d..135a568b4d7c 100644 --- a/sdk/cosmosdb/cosmos/src/utils/batch.ts +++ b/sdk/cosmosdb/cosmos/src/utils/batch.ts @@ -29,7 +29,7 @@ export interface OperationResponse { resourceBody?: JSONObject; } -export function isKeyInRange(min: string, max: string, key: string) { +export function isKeyInRange(min: string, max: string, key: string): boolean { const isAfterMinInclusive = key.localeCompare(min) >= 0; const isBeforeMax = key.localeCompare(max) < 0; return isAfterMinInclusive && isBeforeMax; @@ -58,7 +58,7 @@ export type OperationInput = | ReplaceOperationInput; export interface CreateOperationInput { - partitionKey?: string | number | null | {} | undefined; + partitionKey?: string | number | null | Record | undefined; ifMatch?: string; ifNoneMatch?: string; operationType: typeof BulkOperationType.Create; @@ -66,7 +66,7 @@ export interface CreateOperationInput { } export interface UpsertOperationInput { - partitionKey?: string | number | null | {} | undefined; + partitionKey?: string | number | null | Record | undefined; ifMatch?: string; ifNoneMatch?: string; operationType: typeof BulkOperationType.Upsert; @@ -74,19 +74,19 @@ export interface UpsertOperationInput { } export interface ReadOperationInput { - partitionKey?: string | number | null | {} | undefined; + partitionKey?: string | number | boolean | null | Record | undefined; operationType: typeof BulkOperationType.Read; id: string; } export interface DeleteOperationInput { - partitionKey?: string | number | null | {} | undefined; + partitionKey?: string | number | null | Record | undefined; operationType: typeof BulkOperationType.Delete; id: string; } export interface ReplaceOperationInput { - partitionKey?: string | number | null | {} | undefined; + partitionKey?: string | number | null | Record | undefined; ifMatch?: string; ifNoneMatch?: string; operationType: typeof BulkOperationType.Replace; @@ -126,7 +126,7 @@ export function hasResource( return (operation as OperationWithItem).resourceBody !== undefined; } -export function getPartitionKeyToHash(operation: Operation, partitionProperty: string) { +export function getPartitionKeyToHash(operation: Operation, partitionProperty: string): any { const toHashKey = hasResource(operation) ? (operation.resourceBody as any)[partitionProperty] : (operation.partitionKey && operation.partitionKey.replace(/[[\]"']/g, "")) || diff --git a/sdk/cosmosdb/cosmos/src/utils/checkURL.ts b/sdk/cosmosdb/cosmos/src/utils/checkURL.ts new file mode 100644 index 000000000000..a4ee20dd8c98 --- /dev/null +++ b/sdk/cosmosdb/cosmos/src/utils/checkURL.ts @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { URL } from "./url"; + +export function checkURL(testString: string): URL { + return new URL(testString); +} diff --git a/sdk/cosmosdb/cosmos/src/utils/digest.browser.ts b/sdk/cosmosdb/cosmos/src/utils/digest.browser.ts index 8456479416ed..b5a7cfa27218 100644 --- a/sdk/cosmosdb/cosmos/src/utils/digest.browser.ts +++ b/sdk/cosmosdb/cosmos/src/utils/digest.browser.ts @@ -4,13 +4,13 @@ import { encodeUTF8 } from "./encode"; import { globalCrypto } from "./globalCrypto"; -export async function digest(str: string) { +export async function digest(str: string): Promise { const data = encodeUTF8(str); const hash = await globalCrypto.subtle.digest("SHA-256", data); return bufferToHex(hash); } -function bufferToHex(buffer: ArrayBuffer) { +function bufferToHex(buffer: ArrayBuffer): string { return Array.prototype.map .call(new Uint8Array(buffer), (item: number) => ("00" + item.toString(16)).slice(-2)) .join(""); diff --git a/sdk/cosmosdb/cosmos/src/utils/digest.ts b/sdk/cosmosdb/cosmos/src/utils/digest.ts index 91dfe7797af6..9dedd0d7f2f8 100644 --- a/sdk/cosmosdb/cosmos/src/utils/digest.ts +++ b/sdk/cosmosdb/cosmos/src/utils/digest.ts @@ -3,7 +3,7 @@ import { createHash } from "crypto"; -export async function digest(str: string) { +export async function digest(str: string): Promise { const hash = createHash("sha256"); hash.update(str, "utf8"); return hash.digest("hex"); diff --git a/sdk/cosmosdb/cosmos/src/utils/globalCrypto.native.ts b/sdk/cosmosdb/cosmos/src/utils/globalCrypto.native.ts index e9745952c31c..8a7795c2700f 100644 --- a/sdk/cosmosdb/cosmos/src/utils/globalCrypto.native.ts +++ b/sdk/cosmosdb/cosmos/src/utils/globalCrypto.native.ts @@ -1,6 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +// isomorphic-webcrypto is not listed as a dependency in package.json because +// doing so requires adding a bunch of react packages as peer dependencies. So, +// it is being loaded dynamically here to not cause compiler error. // tslint:disable-next-line:no-var-requires -const globalCrypto = require("isomorphic-webcrypto"); +const globalCrypto = require("isomorphic-webcrypto"); // eslint-disable-line import/no-extraneous-dependencies, @typescript-eslint/no-require-imports export { globalCrypto }; diff --git a/sdk/cosmosdb/cosmos/src/utils/globalCrypto.ts b/sdk/cosmosdb/cosmos/src/utils/globalCrypto.ts index ab7f046745d6..551298e93f01 100644 --- a/sdk/cosmosdb/cosmos/src/utils/globalCrypto.ts +++ b/sdk/cosmosdb/cosmos/src/utils/globalCrypto.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +// eslint-disable-next-line @azure/azure-sdk/ts-no-window const globalRef: any = typeof self === "undefined" ? window : self; if (!globalRef) { diff --git a/sdk/cosmosdb/cosmos/src/utils/hashObject.ts b/sdk/cosmosdb/cosmos/src/utils/hashObject.ts index d88862c03444..b06c57f83831 100644 --- a/sdk/cosmosdb/cosmos/src/utils/hashObject.ts +++ b/sdk/cosmosdb/cosmos/src/utils/hashObject.ts @@ -4,7 +4,7 @@ import { digest } from "./digest"; import stableStringify from "fast-json-stable-stringify"; -export async function hashObject(object: any) { +export async function hashObject(object: unknown): Promise { const stringifiedObject = stableStringify(object); return digest(stringifiedObject); } diff --git a/sdk/cosmosdb/cosmos/src/utils/hashing/encoding/number.ts b/sdk/cosmosdb/cosmos/src/utils/hashing/encoding/number.ts index 40e5a938d5d6..dd1baf160286 100644 --- a/sdk/cosmosdb/cosmos/src/utils/hashing/encoding/number.ts +++ b/sdk/cosmosdb/cosmos/src/utils/hashing/encoding/number.ts @@ -4,7 +4,7 @@ import JSBI from "jsbi"; import { BytePrefix } from "./prefix"; -export function writeNumberForBinaryEncodingJSBI(hash: number) { +export function writeNumberForBinaryEncodingJSBI(hash: number): Buffer { let payload = encodeNumberAsUInt64JSBI(hash); let outputStream = Buffer.from(BytePrefix.Number, "hex"); const firstChunk = JSBI.asUintN(64, JSBI.signedRightShift(payload, JSBI.BigInt(56))); @@ -47,7 +47,7 @@ export function writeNumberForBinaryEncodingJSBI(hash: number) { return outputStream; } -function encodeNumberAsUInt64JSBI(value: number) { +function encodeNumberAsUInt64JSBI(value: number): JSBI { const rawValueBits = getRawBitsJSBI(value); const mask = JSBI.BigInt(0x8000000000000000); const returned = @@ -57,7 +57,7 @@ function encodeNumberAsUInt64JSBI(value: number) { return returned; } -export function doubleToByteArrayJSBI(double: number) { +export function doubleToByteArrayJSBI(double: number): Buffer { const output: Buffer = Buffer.alloc(8); const lng = getRawBitsJSBI(double); for (let i = 0; i < 8; i++) { @@ -71,13 +71,13 @@ export function doubleToByteArrayJSBI(double: number) { return output; } -function getRawBitsJSBI(value: number) { +function getRawBitsJSBI(value: number): JSBI { const view = new DataView(new ArrayBuffer(8)); view.setFloat64(0, value); return JSBI.BigInt(`0x${buf2hex(view.buffer)}`); } -function buf2hex(buffer: ArrayBuffer) { +function buf2hex(buffer: ArrayBuffer): string { return Array.prototype.map .call(new Uint8Array(buffer), (x: number) => ("00" + x.toString(16)).slice(-2)) .join(""); diff --git a/sdk/cosmosdb/cosmos/src/utils/hashing/encoding/string.ts b/sdk/cosmosdb/cosmos/src/utils/hashing/encoding/string.ts index 39d34025872f..19ff92199aec 100644 --- a/sdk/cosmosdb/cosmos/src/utils/hashing/encoding/string.ts +++ b/sdk/cosmosdb/cosmos/src/utils/hashing/encoding/string.ts @@ -3,7 +3,7 @@ import { BytePrefix } from "./prefix"; -export function writeStringForBinaryEncoding(payload: string) { +export function writeStringForBinaryEncoding(payload: string): Buffer { let outputStream = Buffer.from(BytePrefix.String, "hex"); const MAX_STRING_BYTES_TO_APPEND = 100; const byteArray = [...Buffer.from(payload)]; diff --git a/sdk/cosmosdb/cosmos/src/utils/hashing/v1.ts b/sdk/cosmosdb/cosmos/src/utils/hashing/v1.ts index 313b987d0d60..81ed60e35391 100644 --- a/sdk/cosmosdb/cosmos/src/utils/hashing/v1.ts +++ b/sdk/cosmosdb/cosmos/src/utils/hashing/v1.ts @@ -8,7 +8,7 @@ import MurmurHash from "./murmurHash"; const MAX_STRING_CHARS = 100; -type v1Key = string | number | null | {} | undefined; +type v1Key = string | number | boolean | null | Record | undefined; export function hashV1PartitionKey(partitionKey: v1Key): string { const toHash = prefixKeyByType(partitionKey); @@ -23,7 +23,7 @@ export function hashV1PartitionKey(partitionKey: v1Key): string { function prefixKeyByType(key: v1Key): Buffer { let bytes: Buffer; switch (typeof key) { - case "string": + case "string": { const truncated = key.substr(0, MAX_STRING_CHARS); bytes = Buffer.concat([ Buffer.from(BytePrefix.String, "hex"), @@ -31,20 +31,25 @@ function prefixKeyByType(key: v1Key): Buffer { Buffer.from(BytePrefix.Undefined, "hex") ]); return bytes; - case "number": + } + case "number": { const numberBytes = doubleToByteArrayJSBI(key); bytes = Buffer.concat([Buffer.from(BytePrefix.Number, "hex"), numberBytes]); return bytes; - case "boolean": + } + case "boolean": { const prefix = key ? BytePrefix.True : BytePrefix.False; return Buffer.from(prefix, "hex"); - case "object": + } + case "object": { if (key === null) { return Buffer.from(BytePrefix.Null, "hex"); } return Buffer.from(BytePrefix.Undefined, "hex"); - case "undefined": + } + case "undefined": { return Buffer.from(BytePrefix.Undefined, "hex"); + } default: throw new Error(`Unexpected type: ${typeof key}`); } @@ -52,15 +57,18 @@ function prefixKeyByType(key: v1Key): Buffer { function encodeByType(key: v1Key): Buffer { switch (typeof key) { - case "string": + case "string": { const truncated = key.substr(0, MAX_STRING_CHARS); return writeStringForBinaryEncoding(truncated); - case "number": + } + case "number": { const encodedJSBI = writeNumberForBinaryEncodingJSBI(key); return encodedJSBI; - case "boolean": + } + case "boolean": { const prefix = key ? BytePrefix.True : BytePrefix.False; return Buffer.from(prefix, "hex"); + } case "object": if (key === null) { return Buffer.from(BytePrefix.Null, "hex"); diff --git a/sdk/cosmosdb/cosmos/src/utils/hashing/v2.ts b/sdk/cosmosdb/cosmos/src/utils/hashing/v2.ts index f5a88bac9edc..015356a8f271 100644 --- a/sdk/cosmosdb/cosmos/src/utils/hashing/v2.ts +++ b/sdk/cosmosdb/cosmos/src/utils/hashing/v2.ts @@ -5,7 +5,7 @@ import { doubleToByteArrayJSBI } from "./encoding/number"; import { BytePrefix } from "./encoding/prefix"; import MurmurHash from "./murmurHash"; -type v2Key = string | number | null | {} | undefined; +type v2Key = string | number | boolean | null | Record | undefined; export function hashV2PartitionKey(partitionKey: v2Key): string { const toHash = prefixKeyByType(partitionKey); @@ -18,33 +18,38 @@ export function hashV2PartitionKey(partitionKey: v2Key): string { function prefixKeyByType(key: v2Key): Buffer { let bytes: Buffer; switch (typeof key) { - case "string": + case "string": { bytes = Buffer.concat([ Buffer.from(BytePrefix.String, "hex"), Buffer.from(key), Buffer.from(BytePrefix.Infinity, "hex") ]); return bytes; - case "number": + } + case "number": { const numberBytes = doubleToByteArrayJSBI(key); bytes = Buffer.concat([Buffer.from(BytePrefix.Number, "hex"), numberBytes]); return bytes; - case "boolean": + } + case "boolean": { const prefix = key ? BytePrefix.True : BytePrefix.False; return Buffer.from(prefix, "hex"); - case "object": + } + case "object": { if (key === null) { return Buffer.from(BytePrefix.Null, "hex"); } return Buffer.from(BytePrefix.Undefined, "hex"); - case "undefined": + } + case "undefined": { return Buffer.from(BytePrefix.Undefined, "hex"); + } default: throw new Error(`Unexpected type: ${typeof key}`); } } -export function reverse(buff: Buffer) { +export function reverse(buff: Buffer): Buffer { const buffer = Buffer.allocUnsafe(buff.length); for (let i = 0, j = buff.length - 1; i <= j; ++i, --j) { diff --git a/sdk/cosmosdb/cosmos/src/utils/headers.ts b/sdk/cosmosdb/cosmos/src/utils/headers.ts index 0e596def161f..d504e4170294 100644 --- a/sdk/cosmosdb/cosmos/src/utils/headers.ts +++ b/sdk/cosmosdb/cosmos/src/utils/headers.ts @@ -10,7 +10,9 @@ export async function generateHeaders( resourceType: ResourceType = ResourceType.none, resourceId: string = "", date = new Date() -) { +): Promise<{ + [x: string]: string; +}> { const sig = await signature(masterKey, method, resourceType, resourceId, date); return { @@ -25,7 +27,7 @@ async function signature( resourceType: ResourceType, resourceId: string = "", date = new Date() -) { +): Promise { const type = "master"; const version = "1.0"; const text = diff --git a/sdk/cosmosdb/cosmos/src/utils/hmac.browser.ts b/sdk/cosmosdb/cosmos/src/utils/hmac.browser.ts index 939648ed6c50..0b26cf4fae43 100644 --- a/sdk/cosmosdb/cosmos/src/utils/hmac.browser.ts +++ b/sdk/cosmosdb/cosmos/src/utils/hmac.browser.ts @@ -5,7 +5,7 @@ import { encodeUTF8, encodeBase64 } from "./encode"; import atob from "./atob"; import { globalCrypto } from "./globalCrypto"; -export async function hmac(key: string, message: string) { +export async function hmac(key: string, message: string): Promise { const importParams: HmacImportParams = { name: "HMAC", hash: { name: "SHA-256" } }; const encodedMessage = new Uint8Array( [...unescape(encodeURIComponent(message))].map((c) => c.charCodeAt(0)) diff --git a/sdk/cosmosdb/cosmos/src/utils/hmac.ts b/sdk/cosmosdb/cosmos/src/utils/hmac.ts index 7349e0d5e067..9a5bb1f9e344 100644 --- a/sdk/cosmosdb/cosmos/src/utils/hmac.ts +++ b/sdk/cosmosdb/cosmos/src/utils/hmac.ts @@ -3,7 +3,7 @@ import { createHmac } from "crypto"; -export async function hmac(key: string, message: string) { +export async function hmac(key: string, message: string): Promise { return createHmac("sha256", Buffer.from(key, "base64")) .update(message) .digest("base64"); diff --git a/sdk/cosmosdb/cosmos/src/utils/url.browser.ts b/sdk/cosmosdb/cosmos/src/utils/url.browser.ts index efee868f358c..2b9e17d383bd 100644 --- a/sdk/cosmosdb/cosmos/src/utils/url.browser.ts +++ b/sdk/cosmosdb/cosmos/src/utils/url.browser.ts @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -export function checkURL(testString: string): URL { - return new URL(testString); -} +const url = URL; + +export { url as URL }; diff --git a/sdk/cosmosdb/cosmos/src/utils/url.ts b/sdk/cosmosdb/cosmos/src/utils/url.ts index 9ba0e4887012..71fcc22b49c0 100644 --- a/sdk/cosmosdb/cosmos/src/utils/url.ts +++ b/sdk/cosmosdb/cosmos/src/utils/url.ts @@ -1,13 +1,4 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -export function checkURL(testString: string): URL { - let _URL; - // TODO: Remove this when we drop Node 8 support. URL is global in Node 10+ - if (typeof URL === "undefined") { - // tslint:disable-next-line:no-var-requires - _URL = require("url").URL; - } else { - _URL = URL; - } - return new _URL(testString); -} + +export { URL } from "url"; diff --git a/sdk/cosmosdb/cosmos/test/internal/unit/helper.spec.ts b/sdk/cosmosdb/cosmos/test/internal/unit/helper.spec.ts index c17888362c9b..1500b97d92b2 100644 --- a/sdk/cosmosdb/cosmos/test/internal/unit/helper.spec.ts +++ b/sdk/cosmosdb/cosmos/test/internal/unit/helper.spec.ts @@ -1,18 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. import assert from "assert"; -import { isResourceValid, parseConnectionString } from "../../../src/common"; +import { parseConnectionString } from "../../../src/common"; describe("Helper methods", function() { - describe("isResourceValid Unit Tests", function() { - it("id is not string", function() { - const err = {}; - const result = isResourceValid({ id: 1 }, err); - - assert.equal(result, false); - assert.deepEqual(err, { message: "Id must be a string." }); - }); - }); describe("parseConnectionString", function() { it("parses a valid connection string", function() { const connectionString = diff --git a/sdk/cosmosdb/cosmos/test/internal/unit/inMemoryCollectionRoutingMap.spec.ts b/sdk/cosmosdb/cosmos/test/internal/unit/inMemoryCollectionRoutingMap.spec.ts index 304b2f5de042..fe1bef403f9e 100644 --- a/sdk/cosmosdb/cosmos/test/internal/unit/inMemoryCollectionRoutingMap.spec.ts +++ b/sdk/cosmosdb/cosmos/test/internal/unit/inMemoryCollectionRoutingMap.spec.ts @@ -112,10 +112,10 @@ describe("InMemoryCollectionRoutingMap Tests", function() { // TODO: bad practice to test implementation details it("validate _orderedPartitionInfo", function() { - assert.equal(0, collectionRoutingMap.orderedPartitionInfo[0]); - assert.equal(1, collectionRoutingMap.orderedPartitionInfo[1]); - assert.equal(2, collectionRoutingMap.orderedPartitionInfo[2]); - assert.equal(3, collectionRoutingMap.orderedPartitionInfo[3]); + assert.equal(0, (collectionRoutingMap.orderedPartitionInfo as number[])[0]); + assert.equal(1, (collectionRoutingMap.orderedPartitionInfo as number[])[1]); + assert.equal(2, (collectionRoutingMap.orderedPartitionInfo as number[])[2]); + assert.equal(3, (collectionRoutingMap.orderedPartitionInfo as number[])[3]); }); it("validate getOverlappingRanges", function() {