Refactor to Unified RequestContext (#272)
This commit is contained in:
Родитель
c235b2a312
Коммит
9e8ea2490d
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"mocha.files.glob":"test/legacy/**/*.js",
|
||||
"editor.formatOnSave": true
|
||||
"editor.formatOnSave": true,
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
|
@ -132,15 +132,50 @@
|
|||
"integrity": "sha512-L/srhENhBtbZLUD9FfJ2ZQdnYv3A3MT3UI2EMbC06fHUzIxLjjbkomD6o42UrbsRMwlS9p1BtxExeaCdX73q2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"@sinonjs/formatio": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz",
|
||||
"integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==",
|
||||
"@sinonjs/commons": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.4.0.tgz",
|
||||
"integrity": "sha512-9jHK3YF/8HtJ9wCAbG+j8cD0i0+ATS9A7gXFqS36TblLPNy6rEEc+SB0imo91eCboGaBYGV/MT1/br/J+EE7Tw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"samsam": "1.3.0"
|
||||
"type-detect": "4.0.8"
|
||||
}
|
||||
},
|
||||
"@sinonjs/formatio": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.1.tgz",
|
||||
"integrity": "sha512-tsHvOB24rvyvV2+zKMmPkZ7dXX6LSLKZ7aOtXY6Edklp0uRcgGpOsQTTGTcWViFyx4uhWc6GV8QdnALbIbIdeQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@sinonjs/commons": "^1",
|
||||
"@sinonjs/samsam": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"@sinonjs/samsam": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.0.tgz",
|
||||
"integrity": "sha512-beHeJM/RRAaLLsMJhsCvHK31rIqZuobfPLa/80yGH5hnD8PV1hyh9xJBJNFfNmO7yWqm+zomijHsXpI6iTQJfQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@sinonjs/commons": "^1.0.2",
|
||||
"array-from": "^2.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "4.17.11",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
||||
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@sinonjs/text-encoding": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz",
|
||||
"integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/argparse": {
|
||||
"version": "1.0.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.33.tgz",
|
||||
|
@ -253,9 +288,9 @@
|
|||
}
|
||||
},
|
||||
"@types/sinon": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-4.3.3.tgz",
|
||||
"integrity": "sha512-Tt7w/ylBS/OEAlSCwzB0Db1KbxnkycP/1UkQpbvKFYoUuRn4uYsC3xh5TRPrOjTy0i8TIkSz1JdNL4GPVdf3KQ==",
|
||||
"version": "7.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.0.10.tgz",
|
||||
"integrity": "sha512-4w7SvsiUOtd4mUfund9QROPSJ5At/GQskDpqd87pJIRI6ULWSJqHI3GIZE337wQuN3aznroJGr94+o8fwvL37Q==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/tunnel": {
|
||||
|
@ -373,6 +408,12 @@
|
|||
"integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
|
||||
"dev": true
|
||||
},
|
||||
"array-from": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz",
|
||||
"integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=",
|
||||
"dev": true
|
||||
},
|
||||
"array-slice": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
|
||||
|
@ -2943,9 +2984,9 @@
|
|||
}
|
||||
},
|
||||
"just-extend": {
|
||||
"version": "1.1.27",
|
||||
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.27.tgz",
|
||||
"integrity": "sha512-mJVp13Ix6gFo3SBAy9U/kL+oeZqzlYYYLQBwXVBlVzIsZwBqGREnOro24oC/8s8aox+rJhtZ2DiQof++IrkA+g==",
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz",
|
||||
"integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==",
|
||||
"dev": true
|
||||
},
|
||||
"karma": {
|
||||
|
@ -3200,9 +3241,9 @@
|
|||
}
|
||||
},
|
||||
"lolex": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.1.tgz",
|
||||
"integrity": "sha512-Oo2Si3RMKV3+lV5MsSWplDQFoTClz/24S0MMHYcgGWWmFXr6TMlqcqk/l1GtH+d5wLBwNRiqGnwDRMirtFalJw==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lolex/-/lolex-3.1.0.tgz",
|
||||
"integrity": "sha512-zFo5MgCJ0rZ7gQg69S4pqBsLURbFw11X68C18OcJjJQbqaXm2NoTrGl1IMM3TIz0/BnN1tIs2tzmmqvCsOMMjw==",
|
||||
"dev": true
|
||||
},
|
||||
"loud-rejection": {
|
||||
|
@ -3589,16 +3630,24 @@
|
|||
"dev": true
|
||||
},
|
||||
"nise": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/nise/-/nise-1.4.2.tgz",
|
||||
"integrity": "sha512-BxH/DxoQYYdhKgVAfqVy4pzXRZELHOIewzoesxpjYvpU+7YOalQhGNPf7wAx8pLrTNPrHRDlLOkAl8UI0ZpXjw==",
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/nise/-/nise-1.4.10.tgz",
|
||||
"integrity": "sha512-sa0RRbj53dovjc7wombHmVli9ZihXbXCQ2uH3TNm03DyvOSIQbxg+pbqDKrk2oxMK1rtLGVlKxcB9rrc6X5YjA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@sinonjs/formatio": "^2.0.0",
|
||||
"just-extend": "^1.1.27",
|
||||
"@sinonjs/formatio": "^3.1.0",
|
||||
"@sinonjs/text-encoding": "^0.7.1",
|
||||
"just-extend": "^4.0.2",
|
||||
"lolex": "^2.3.2",
|
||||
"path-to-regexp": "^1.7.0",
|
||||
"text-encoding": "^0.6.4"
|
||||
"path-to-regexp": "^1.7.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"lolex": {
|
||||
"version": "2.7.5",
|
||||
"resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz",
|
||||
"integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
|
@ -4354,12 +4403,6 @@
|
|||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"dev": true
|
||||
},
|
||||
"samsam": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz",
|
||||
"integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==",
|
||||
"dev": true
|
||||
},
|
||||
"semaphore": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.0.5.tgz",
|
||||
|
@ -4448,24 +4491,24 @@
|
|||
"dev": true
|
||||
},
|
||||
"sinon": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-5.1.1.tgz",
|
||||
"integrity": "sha512-h/3uHscbt5pQNxkf7Y/Lb9/OM44YNCicHakcq73ncbrIS8lXg+ZGOZbtuU+/km4YnyiCYfQQEwANaReJz7KDfw==",
|
||||
"version": "7.2.7",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-7.2.7.tgz",
|
||||
"integrity": "sha512-rlrre9F80pIQr3M36gOdoCEWzFAMDgHYD8+tocqOw+Zw9OZ8F84a80Ds69eZfcjnzDqqG88ulFld0oin/6rG/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@sinonjs/formatio": "^2.0.0",
|
||||
"@sinonjs/commons": "^1.3.1",
|
||||
"@sinonjs/formatio": "^3.2.1",
|
||||
"@sinonjs/samsam": "^3.2.0",
|
||||
"diff": "^3.5.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lolex": "^2.4.2",
|
||||
"nise": "^1.3.3",
|
||||
"supports-color": "^5.4.0",
|
||||
"type-detect": "^4.0.8"
|
||||
"lolex": "^3.1.0",
|
||||
"nise": "^1.4.10",
|
||||
"supports-color": "^5.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"supports-color": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
|
||||
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
|
@ -4801,12 +4844,6 @@
|
|||
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
|
||||
"dev": true
|
||||
},
|
||||
"text-encoding": {
|
||||
"version": "0.6.4",
|
||||
"resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
|
||||
"integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=",
|
||||
"dev": true
|
||||
},
|
||||
"thunkify": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz",
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
"@types/node-fetch": "2.1.7",
|
||||
"@types/priorityqueuejs": "^1.0.1",
|
||||
"@types/semaphore": "^1.1.0",
|
||||
"@types/sinon": "^4.3.3",
|
||||
"@types/sinon": "7.0.10",
|
||||
"@types/tunnel": "^0.0.0",
|
||||
"@types/underscore": "^1.8.8",
|
||||
"cross-env": "5.2.0",
|
||||
|
@ -66,7 +66,7 @@
|
|||
"rollup-plugin-json": "3.1.0",
|
||||
"rollup-plugin-local-resolve": "^1.0.7",
|
||||
"rollup-plugin-multi-entry": "2.0.2",
|
||||
"sinon": "^5.1.1",
|
||||
"sinon": "7.2.7",
|
||||
"source-map-support": "0.5.11",
|
||||
"ts-node": "^8.0.2",
|
||||
"tslint": "5.11.0",
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import { PartitionKeyRange } from "./client/Container/PartitionKeyRange";
|
||||
import { Resource } from "./client/Resource";
|
||||
import { Constants, HTTPMethod, OperationType, ResourceType } from "./common/constants";
|
||||
import { getIdFromLink, getPathFromLink, parseLink, setIsUpsertHeader } from "./common/helper";
|
||||
import { getIdFromLink, getPathFromLink, parseLink } from "./common/helper";
|
||||
import { StatusCodes, SubStatusCodes } from "./common/statusCodes";
|
||||
import { CosmosClientOptions } from "./CosmosClientOptions";
|
||||
import { ConnectionPolicy, ConsistencyLevel, DatabaseAccount, QueryCompatibilityMode } from "./documents";
|
||||
import { Agent, CosmosClientOptions } from "./CosmosClientOptions";
|
||||
import { ConnectionPolicy, ConsistencyLevel, DatabaseAccount } from "./documents";
|
||||
import { GlobalEndpointManager } from "./globalEndpointManager";
|
||||
import { FetchFunctionCallback, SqlQuerySpec } from "./queryExecutionContext";
|
||||
import { CosmosHeaders } from "./queryExecutionContext/CosmosHeaders";
|
||||
import { QueryIterator } from "./queryIterator";
|
||||
import { FeedOptions, RequestHandler, RequestOptions, Response } from "./request";
|
||||
import { FeedOptions, RequestOptions, Response } from "./request";
|
||||
import { ErrorResponse } from "./request";
|
||||
import { getHeaders } from "./request/request";
|
||||
import { RequestContext } from "./request/RequestContext";
|
||||
import { request as executeRequest } from "./request/RequestHandler";
|
||||
import { SessionContainer } from "./session/sessionContainer";
|
||||
import { SessionContext } from "./session/SessionContext";
|
||||
|
||||
|
@ -23,7 +24,6 @@ import { SessionContext } from "./session/SessionContext";
|
|||
export class ClientContext {
|
||||
private readonly sessionContainer: SessionContainer;
|
||||
private connectionPolicy: ConnectionPolicy;
|
||||
private requestHandler: RequestHandler;
|
||||
|
||||
public partitionKeyDefinitionCache: { [containerUrl: string]: any }; // TODO: ParitionKeyDefinitionCache
|
||||
public constructor(
|
||||
|
@ -32,45 +32,35 @@ export class ClientContext {
|
|||
) {
|
||||
this.connectionPolicy = cosmosClientOptions.connectionPolicy;
|
||||
this.sessionContainer = new SessionContainer();
|
||||
this.requestHandler = new RequestHandler(
|
||||
globalEndpointManager,
|
||||
this.connectionPolicy,
|
||||
this.cosmosClientOptions.agent
|
||||
);
|
||||
this.partitionKeyDefinitionCache = {};
|
||||
}
|
||||
/** @ignore */
|
||||
public async read<T>(
|
||||
path: string,
|
||||
type: ResourceType,
|
||||
id: string,
|
||||
initialHeaders: CosmosHeaders,
|
||||
options?: RequestOptions
|
||||
resourceType: ResourceType,
|
||||
resourceId: string,
|
||||
options: RequestOptions = {}
|
||||
): Promise<Response<T & Resource>> {
|
||||
try {
|
||||
const requestHeaders = await getHeaders(
|
||||
this.cosmosClientOptions.auth,
|
||||
{ ...initialHeaders, ...this.cosmosClientOptions.defaultHeaders, ...(options && options.initialHeaders) },
|
||||
HTTPMethod.get,
|
||||
path,
|
||||
id,
|
||||
type,
|
||||
options,
|
||||
undefined,
|
||||
this.cosmosClientOptions.connectionPolicy.useMultipleWriteLocations
|
||||
);
|
||||
this.applySessionToken(path, requestHeaders);
|
||||
|
||||
const request: any = {
|
||||
// TODO: any
|
||||
const request: RequestContext = {
|
||||
globalEndpointManager: this.globalEndpointManager,
|
||||
requestAgent: this.cosmosClientOptions.agent,
|
||||
connectionPolicy: this.connectionPolicy,
|
||||
method: HTTPMethod.get,
|
||||
path,
|
||||
operationType: OperationType.Read,
|
||||
client: this,
|
||||
endpointOverride: null
|
||||
resourceId,
|
||||
options,
|
||||
resourceType
|
||||
};
|
||||
|
||||
request.headers = await this.buildHeaders(request);
|
||||
this.applySessionToken(request);
|
||||
|
||||
// read will use ReadEndpoint since it uses GET operation
|
||||
const endpoint = await this.globalEndpointManager.resolveServiceEndpoint(request);
|
||||
const response = await this.requestHandler.get(endpoint, request, requestHeaders);
|
||||
request.endpoint = await this.globalEndpointManager.resolveServiceEndpoint(request);
|
||||
const response = await executeRequest<T & Resource>(request);
|
||||
this.captureSessionToken(undefined, path, OperationType.Read, response.headers);
|
||||
return response;
|
||||
} catch (err) {
|
||||
|
@ -81,8 +71,8 @@ export class ClientContext {
|
|||
|
||||
public async queryFeed<T>(
|
||||
path: string,
|
||||
type: ResourceType,
|
||||
id: string,
|
||||
resourceType: ResourceType,
|
||||
resourceId: string,
|
||||
resultFn: (result: { [key: string]: any }) => any[], // TODO: any
|
||||
query: SqlQuerySpec | string,
|
||||
options: FeedOptions,
|
||||
|
@ -91,67 +81,37 @@ export class ClientContext {
|
|||
// Query operations will use ReadEndpoint even though it uses
|
||||
// GET(for queryFeed) and POST(for regular query operations)
|
||||
|
||||
const request: any = {
|
||||
// TODO: any request
|
||||
const request: RequestContext = {
|
||||
globalEndpointManager: this.globalEndpointManager,
|
||||
requestAgent: this.cosmosClientOptions.agent,
|
||||
connectionPolicy: this.connectionPolicy,
|
||||
method: HTTPMethod.get,
|
||||
path,
|
||||
operationType: OperationType.Query,
|
||||
client: this,
|
||||
endpointOverride: null
|
||||
partitionKeyRangeId,
|
||||
resourceId,
|
||||
resourceType,
|
||||
options,
|
||||
body: query
|
||||
};
|
||||
|
||||
const endpoint = await this.globalEndpointManager.resolveServiceEndpoint(request);
|
||||
|
||||
const initialHeaders = { ...this.cosmosClientOptions.defaultHeaders, ...(options && options.initialHeaders) };
|
||||
if (query === undefined) {
|
||||
const reqHeaders = await getHeaders(
|
||||
this.cosmosClientOptions.auth,
|
||||
initialHeaders,
|
||||
HTTPMethod.get,
|
||||
path,
|
||||
id,
|
||||
type,
|
||||
options,
|
||||
partitionKeyRangeId,
|
||||
this.cosmosClientOptions.connectionPolicy.useMultipleWriteLocations
|
||||
);
|
||||
this.applySessionToken(path, reqHeaders);
|
||||
|
||||
const response = await this.requestHandler.get(endpoint, request, reqHeaders);
|
||||
this.captureSessionToken(undefined, path, OperationType.Query, response.headers);
|
||||
return this.processQueryFeedResponse(response, !!query, resultFn);
|
||||
} else {
|
||||
initialHeaders[Constants.HttpHeaders.IsQuery] = "true";
|
||||
switch (this.cosmosClientOptions.queryCompatibilityMode) {
|
||||
case QueryCompatibilityMode.SqlQuery:
|
||||
initialHeaders[Constants.HttpHeaders.ContentType] = Constants.MediaTypes.SQL;
|
||||
break;
|
||||
case QueryCompatibilityMode.Query:
|
||||
case QueryCompatibilityMode.Default:
|
||||
default:
|
||||
if (typeof query === "string") {
|
||||
query = { query }; // Converts query text to query object.
|
||||
}
|
||||
initialHeaders[Constants.HttpHeaders.ContentType] = Constants.MediaTypes.QueryJson;
|
||||
break;
|
||||
}
|
||||
|
||||
const reqHeaders = await getHeaders(
|
||||
this.cosmosClientOptions.auth,
|
||||
initialHeaders,
|
||||
HTTPMethod.post,
|
||||
path,
|
||||
id,
|
||||
type,
|
||||
options,
|
||||
partitionKeyRangeId,
|
||||
this.cosmosClientOptions.connectionPolicy.useMultipleWriteLocations
|
||||
);
|
||||
this.applySessionToken(path, reqHeaders);
|
||||
|
||||
const response = await this.requestHandler.post(endpoint, request, query, reqHeaders);
|
||||
this.captureSessionToken(undefined, path, OperationType.Query, response.headers);
|
||||
return this.processQueryFeedResponse(response, !!query, resultFn);
|
||||
if (query !== undefined) {
|
||||
request.method = HTTPMethod.post;
|
||||
}
|
||||
request.endpoint = await this.globalEndpointManager.resolveServiceEndpoint(request);
|
||||
request.headers = await this.buildHeaders(request);
|
||||
if (query !== undefined) {
|
||||
request.headers[Constants.HttpHeaders.IsQuery] = "true";
|
||||
request.headers[Constants.HttpHeaders.ContentType] = Constants.MediaTypes.QueryJson;
|
||||
if (typeof query === "string") {
|
||||
request.body = { query }; // Converts query text to query object.
|
||||
}
|
||||
}
|
||||
this.applySessionToken(request);
|
||||
const response = await executeRequest(request);
|
||||
this.captureSessionToken(undefined, path, OperationType.Query, response.headers);
|
||||
return this.processQueryFeedResponse(response, !!query, resultFn);
|
||||
}
|
||||
|
||||
public queryPartitionKeyRanges(collectionLink: string, query?: string | SqlQuerySpec, options?: FeedOptions) {
|
||||
|
@ -165,35 +125,29 @@ export class ClientContext {
|
|||
|
||||
public async delete<T>(
|
||||
path: string,
|
||||
type: ResourceType,
|
||||
id: string,
|
||||
initialHeaders: CosmosHeaders,
|
||||
options?: RequestOptions
|
||||
resourceType: ResourceType,
|
||||
resourceId: string,
|
||||
options: RequestOptions = {}
|
||||
): Promise<Response<T & Resource>> {
|
||||
try {
|
||||
const reqHeaders = await getHeaders(
|
||||
this.cosmosClientOptions.auth,
|
||||
{ ...initialHeaders, ...this.cosmosClientOptions.defaultHeaders, ...(options && options.initialHeaders) },
|
||||
HTTPMethod.delete,
|
||||
path,
|
||||
id,
|
||||
type,
|
||||
options,
|
||||
undefined,
|
||||
this.cosmosClientOptions.connectionPolicy.useMultipleWriteLocations
|
||||
);
|
||||
|
||||
const request: RequestContext = {
|
||||
globalEndpointManager: this.globalEndpointManager,
|
||||
requestAgent: this.cosmosClientOptions.agent,
|
||||
connectionPolicy: this.connectionPolicy,
|
||||
method: HTTPMethod.delete,
|
||||
client: this,
|
||||
operationType: OperationType.Delete,
|
||||
path,
|
||||
resourceType: type
|
||||
resourceType,
|
||||
options,
|
||||
resourceId
|
||||
};
|
||||
|
||||
this.applySessionToken(path, reqHeaders);
|
||||
request.headers = await this.buildHeaders(request);
|
||||
this.applySessionToken(request);
|
||||
// deleteResource will use WriteEndpoint since it uses DELETE operation
|
||||
const endpoint = await this.globalEndpointManager.resolveServiceEndpoint(request);
|
||||
const response = await this.requestHandler.delete(endpoint, request, reqHeaders);
|
||||
request.endpoint = await this.globalEndpointManager.resolveServiceEndpoint(request);
|
||||
const response = await executeRequest<T & Resource>(request);
|
||||
if (parseLink(path).type !== "colls") {
|
||||
this.captureSessionToken(undefined, path, OperationType.Delete, response.headers);
|
||||
} else {
|
||||
|
@ -210,54 +164,47 @@ export class ClientContext {
|
|||
public async create<T>(
|
||||
body: T,
|
||||
path: string,
|
||||
type: ResourceType,
|
||||
id: string,
|
||||
initialHeaders: CosmosHeaders,
|
||||
options?: RequestOptions
|
||||
resourceType: ResourceType,
|
||||
resourceId: string,
|
||||
options: RequestOptions
|
||||
): Promise<Response<T & Resource>>;
|
||||
|
||||
// But a few cases, like permissions, there is additional junk added to the response that isn't in system resource props
|
||||
public async create<T, U>(
|
||||
body: T,
|
||||
path: string,
|
||||
type: ResourceType,
|
||||
id: string,
|
||||
initialHeaders: CosmosHeaders,
|
||||
options?: RequestOptions
|
||||
resourceType: ResourceType,
|
||||
resourceId: string,
|
||||
options: RequestOptions
|
||||
): Promise<Response<T & U & Resource>>;
|
||||
public async create<T, U>(
|
||||
body: T,
|
||||
path: string,
|
||||
type: ResourceType,
|
||||
id: string,
|
||||
initialHeaders: CosmosHeaders,
|
||||
options?: RequestOptions
|
||||
resourceType: ResourceType,
|
||||
resourceId: string,
|
||||
options: RequestOptions = {}
|
||||
): Promise<Response<T & U & Resource>> {
|
||||
try {
|
||||
const requestHeaders = await getHeaders(
|
||||
this.cosmosClientOptions.auth,
|
||||
{ ...initialHeaders, ...this.cosmosClientOptions.defaultHeaders, ...(options && options.initialHeaders) },
|
||||
HTTPMethod.post,
|
||||
path,
|
||||
id,
|
||||
type,
|
||||
options,
|
||||
undefined,
|
||||
this.cosmosClientOptions.connectionPolicy.useMultipleWriteLocations
|
||||
);
|
||||
|
||||
const request: RequestContext = {
|
||||
globalEndpointManager: this.globalEndpointManager,
|
||||
requestAgent: this.cosmosClientOptions.agent,
|
||||
connectionPolicy: this.connectionPolicy,
|
||||
method: HTTPMethod.post,
|
||||
client: this,
|
||||
operationType: OperationType.Create,
|
||||
path,
|
||||
resourceType: type
|
||||
resourceType,
|
||||
resourceId,
|
||||
body,
|
||||
options
|
||||
};
|
||||
|
||||
request.headers = await this.buildHeaders(request);
|
||||
// create will use WriteEndpoint since it uses POST operation
|
||||
this.applySessionToken(path, requestHeaders);
|
||||
this.applySessionToken(request);
|
||||
|
||||
const endpoint = await this.globalEndpointManager.resolveServiceEndpoint(request);
|
||||
const response = await this.requestHandler.post(endpoint, request, body, requestHeaders);
|
||||
request.endpoint = await this.globalEndpointManager.resolveServiceEndpoint(request);
|
||||
const response = await executeRequest<T & U & Resource>(request);
|
||||
this.captureSessionToken(undefined, path, OperationType.Create, response.headers);
|
||||
return response;
|
||||
} catch (err) {
|
||||
|
@ -279,14 +226,16 @@ export class ClientContext {
|
|||
}
|
||||
}
|
||||
|
||||
private applySessionToken(path: string, reqHeaders: CosmosHeaders) {
|
||||
const request = this.getSessionParams(path);
|
||||
private applySessionToken(requestContext: RequestContext) {
|
||||
const request = this.getSessionParams(requestContext.path);
|
||||
|
||||
if (reqHeaders && reqHeaders[Constants.HttpHeaders.SessionToken]) {
|
||||
if (requestContext.headers && requestContext.headers[Constants.HttpHeaders.SessionToken]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sessionConsistency: ConsistencyLevel = reqHeaders[Constants.HttpHeaders.ConsistencyLevel] as ConsistencyLevel;
|
||||
const sessionConsistency: ConsistencyLevel = requestContext.headers[
|
||||
Constants.HttpHeaders.ConsistencyLevel
|
||||
] as ConsistencyLevel;
|
||||
if (!sessionConsistency) {
|
||||
return;
|
||||
}
|
||||
|
@ -298,44 +247,39 @@ export class ClientContext {
|
|||
if (request.resourceAddress) {
|
||||
const sessionToken = this.sessionContainer.get(request);
|
||||
if (sessionToken) {
|
||||
reqHeaders[Constants.HttpHeaders.SessionToken] = sessionToken;
|
||||
requestContext.headers[Constants.HttpHeaders.SessionToken] = sessionToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async replace<T>(
|
||||
resource: any,
|
||||
body: any,
|
||||
path: string,
|
||||
type: ResourceType,
|
||||
id: string,
|
||||
initialHeaders: CosmosHeaders,
|
||||
options?: RequestOptions
|
||||
resourceType: ResourceType,
|
||||
resourceId: string,
|
||||
options: RequestOptions = {}
|
||||
): Promise<Response<T & Resource>> {
|
||||
try {
|
||||
const reqHeaders = await getHeaders(
|
||||
this.cosmosClientOptions.auth,
|
||||
{ ...initialHeaders, ...this.cosmosClientOptions.defaultHeaders, ...(options && options.initialHeaders) },
|
||||
HTTPMethod.put,
|
||||
path,
|
||||
id,
|
||||
type,
|
||||
options,
|
||||
undefined,
|
||||
this.cosmosClientOptions.connectionPolicy.useMultipleWriteLocations
|
||||
);
|
||||
|
||||
const request: RequestContext = {
|
||||
globalEndpointManager: this.globalEndpointManager,
|
||||
requestAgent: this.cosmosClientOptions.agent,
|
||||
connectionPolicy: this.connectionPolicy,
|
||||
method: HTTPMethod.put,
|
||||
client: this,
|
||||
operationType: OperationType.Replace,
|
||||
path,
|
||||
resourceType: type
|
||||
resourceType,
|
||||
body,
|
||||
resourceId,
|
||||
options
|
||||
};
|
||||
|
||||
this.applySessionToken(path, reqHeaders);
|
||||
request.headers = await this.buildHeaders(request);
|
||||
this.applySessionToken(request);
|
||||
|
||||
// replace will use WriteEndpoint since it uses PUT operation
|
||||
const endpoint = await this.globalEndpointManager.resolveServiceEndpoint(reqHeaders);
|
||||
const response = await this.requestHandler.put(endpoint, request, resource, reqHeaders);
|
||||
request.endpoint = await this.globalEndpointManager.resolveServiceEndpoint(request);
|
||||
const response = await executeRequest<T & Resource>(request);
|
||||
this.captureSessionToken(undefined, path, OperationType.Replace, response.headers);
|
||||
return response;
|
||||
} catch (err) {
|
||||
|
@ -347,53 +291,46 @@ export class ClientContext {
|
|||
public async upsert<T>(
|
||||
body: T,
|
||||
path: string,
|
||||
type: ResourceType,
|
||||
id: string,
|
||||
initialHeaders: CosmosHeaders,
|
||||
options?: RequestOptions
|
||||
resourceType: ResourceType,
|
||||
resourceId: string,
|
||||
options: RequestOptions
|
||||
): Promise<Response<T & Resource>>;
|
||||
public async upsert<T, U>(
|
||||
body: T,
|
||||
path: string,
|
||||
type: ResourceType,
|
||||
id: string,
|
||||
initialHeaders: CosmosHeaders,
|
||||
options?: RequestOptions
|
||||
resourceType: ResourceType,
|
||||
resourceId: string,
|
||||
options: RequestOptions
|
||||
): Promise<Response<T & U & Resource>>;
|
||||
public async upsert<T>(
|
||||
body: T,
|
||||
path: string,
|
||||
type: ResourceType,
|
||||
id: string,
|
||||
initialHeaders: CosmosHeaders,
|
||||
options?: RequestOptions
|
||||
resourceType: ResourceType,
|
||||
resourceId: string,
|
||||
options: RequestOptions = {}
|
||||
): Promise<Response<T & Resource>> {
|
||||
try {
|
||||
const requestHeaders = await getHeaders(
|
||||
this.cosmosClientOptions.auth,
|
||||
{ ...initialHeaders, ...this.cosmosClientOptions.defaultHeaders, ...(options && options.initialHeaders) },
|
||||
HTTPMethod.post,
|
||||
path,
|
||||
id,
|
||||
type,
|
||||
options,
|
||||
undefined,
|
||||
this.cosmosClientOptions.connectionPolicy.useMultipleWriteLocations
|
||||
);
|
||||
|
||||
const request: RequestContext = {
|
||||
globalEndpointManager: this.globalEndpointManager,
|
||||
requestAgent: this.cosmosClientOptions.agent,
|
||||
connectionPolicy: this.connectionPolicy,
|
||||
method: HTTPMethod.post,
|
||||
client: this,
|
||||
operationType: OperationType.Upsert,
|
||||
path,
|
||||
resourceType: type
|
||||
resourceType,
|
||||
body,
|
||||
resourceId,
|
||||
options
|
||||
};
|
||||
|
||||
setIsUpsertHeader(requestHeaders);
|
||||
this.applySessionToken(path, requestHeaders);
|
||||
request.headers = await this.buildHeaders(request);
|
||||
request.headers[Constants.HttpHeaders.IsUpsert] = true;
|
||||
this.applySessionToken(request);
|
||||
|
||||
// upsert will use WriteEndpoint since it uses POST operation
|
||||
const endpoint = await this.globalEndpointManager.resolveServiceEndpoint(request);
|
||||
const response = await this.requestHandler.post(endpoint, request, body, requestHeaders);
|
||||
request.endpoint = await this.globalEndpointManager.resolveServiceEndpoint(request);
|
||||
const response = await executeRequest<T & Resource>(request);
|
||||
this.captureSessionToken(undefined, path, OperationType.Upsert, response.headers);
|
||||
return response;
|
||||
} catch (err) {
|
||||
|
@ -405,10 +342,8 @@ export class ClientContext {
|
|||
public async execute<T>(
|
||||
sprocLink: string,
|
||||
params?: any[], // TODO: any
|
||||
options?: RequestOptions
|
||||
options: RequestOptions = {}
|
||||
): Promise<Response<T>> {
|
||||
const initialHeaders = { ...this.cosmosClientOptions.defaultHeaders, ...(options && options.initialHeaders) };
|
||||
|
||||
// Accept a single parameter or an array of parameters.
|
||||
// Didn't add type annotation for this because we should legacy this behavior
|
||||
if (params !== null && params !== undefined && !Array.isArray(params)) {
|
||||
|
@ -417,28 +352,24 @@ export class ClientContext {
|
|||
const path = getPathFromLink(sprocLink);
|
||||
const id = getIdFromLink(sprocLink);
|
||||
|
||||
const headers = await getHeaders(
|
||||
this.cosmosClientOptions.auth,
|
||||
initialHeaders,
|
||||
HTTPMethod.post,
|
||||
path,
|
||||
id,
|
||||
ResourceType.sproc,
|
||||
options,
|
||||
undefined,
|
||||
this.cosmosClientOptions.connectionPolicy.useMultipleWriteLocations
|
||||
);
|
||||
|
||||
const request: RequestContext = {
|
||||
globalEndpointManager: this.globalEndpointManager,
|
||||
requestAgent: this.cosmosClientOptions.agent,
|
||||
connectionPolicy: this.connectionPolicy,
|
||||
method: HTTPMethod.post,
|
||||
client: this,
|
||||
operationType: OperationType.Execute,
|
||||
path,
|
||||
resourceType: ResourceType.sproc
|
||||
resourceType: ResourceType.sproc,
|
||||
options,
|
||||
resourceId: id,
|
||||
body: params
|
||||
};
|
||||
|
||||
request.headers = await this.buildHeaders(request);
|
||||
// executeStoredProcedure will use WriteEndpoint since it uses POST operation
|
||||
const endpoint = await this.globalEndpointManager.resolveServiceEndpoint(request);
|
||||
return this.requestHandler.post(endpoint, request, params, headers);
|
||||
request.endpoint = await this.globalEndpointManager.resolveServiceEndpoint(request);
|
||||
return executeRequest<T>(request);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -447,28 +378,23 @@ export class ClientContext {
|
|||
* If not present, current client's url will be used.
|
||||
*/
|
||||
public async getDatabaseAccount(options: RequestOptions = {}): Promise<Response<DatabaseAccount>> {
|
||||
const urlConnection = options.urlConnection || this.cosmosClientOptions.endpoint;
|
||||
|
||||
const requestHeaders = await getHeaders(
|
||||
this.cosmosClientOptions.auth,
|
||||
this.cosmosClientOptions.defaultHeaders,
|
||||
HTTPMethod.get,
|
||||
"",
|
||||
"",
|
||||
ResourceType.none,
|
||||
{},
|
||||
undefined,
|
||||
this.cosmosClientOptions.connectionPolicy.useMultipleWriteLocations
|
||||
);
|
||||
|
||||
const endpoint = options.urlConnection || this.cosmosClientOptions.endpoint;
|
||||
const request: RequestContext = {
|
||||
endpoint,
|
||||
globalEndpointManager: this.globalEndpointManager,
|
||||
requestAgent: this.cosmosClientOptions.agent,
|
||||
connectionPolicy: this.connectionPolicy,
|
||||
method: HTTPMethod.get,
|
||||
client: this,
|
||||
operationType: OperationType.Read,
|
||||
path: "",
|
||||
resourceType: ResourceType.none
|
||||
resourceType: ResourceType.none,
|
||||
options
|
||||
};
|
||||
|
||||
const { result, headers } = await this.requestHandler.get(urlConnection, request, requestHeaders);
|
||||
request.headers = await this.buildHeaders(request);
|
||||
// await options.beforeOperation({ endpoint, request, headers: requestHeaders });
|
||||
const { result, headers } = await executeRequest(request);
|
||||
|
||||
const databaseAccount = new DatabaseAccount(result, headers);
|
||||
|
||||
|
@ -489,7 +415,7 @@ export class ClientContext {
|
|||
operationType: OperationType,
|
||||
resHeaders: CosmosHeaders
|
||||
) {
|
||||
const request = this.getSessionParams(path); // TODO: any request
|
||||
const request = this.getSessionParams(path);
|
||||
request.operationType = operationType;
|
||||
if (
|
||||
!err ||
|
||||
|
@ -539,4 +465,18 @@ export class ClientContext {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
private buildHeaders(requestContext: RequestContext) {
|
||||
return getHeaders({
|
||||
authOptions: this.cosmosClientOptions.auth,
|
||||
defaultHeaders: { ...this.cosmosClientOptions.defaultHeaders, ...requestContext.options.initialHeaders },
|
||||
verb: requestContext.method,
|
||||
path: requestContext.path,
|
||||
resourceId: requestContext.resourceId,
|
||||
resourceType: requestContext.resourceType,
|
||||
options: requestContext.options,
|
||||
partitionKeyRangeId: requestContext.partitionKeyRangeId,
|
||||
useMultipleWriteLocations: this.connectionPolicy.useMultipleWriteLocations
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { AuthOptions } from "./auth";
|
||||
import { ConnectionPolicy, ConsistencyLevel, QueryCompatibilityMode } from "./documents";
|
||||
import { ConnectionPolicy, ConsistencyLevel } from "./documents";
|
||||
import { CosmosHeaders } from "./queryExecutionContext/CosmosHeaders";
|
||||
|
||||
// We expose our own Agent interface to avoid taking a dependency on and leaking node types. This interface should mirror the node Agent interface
|
||||
interface Agent {
|
||||
export interface Agent {
|
||||
maxFreeSockets: number;
|
||||
maxSockets: number;
|
||||
sockets: any;
|
||||
|
@ -31,5 +31,4 @@ export interface CosmosClientOptions {
|
|||
* Use an agent such as https://github.com/TooTallNate/node-proxy-agent if you need to connect to Cosmos via a proxy
|
||||
*/
|
||||
agent?: Agent;
|
||||
queryCompatibilityMode?: QueryCompatibilityMode;
|
||||
}
|
||||
|
|
|
@ -98,7 +98,9 @@ export class LocationCache {
|
|||
);
|
||||
}
|
||||
|
||||
public resolveServiceEndpoint(request: RequestContext): string {
|
||||
public resolveServiceEndpoint(
|
||||
request: Pick<RequestContext, "operationType" | "resourceType" | "retryCount" | "locationRouting">
|
||||
): string {
|
||||
request.locationRouting = request.locationRouting || new LocationRouting();
|
||||
|
||||
let locationIndex = request.locationRouting.locationIndexToRoute || 0;
|
||||
|
|
|
@ -36,7 +36,7 @@ export class Conflict {
|
|||
const path = getPathFromLink(this.url, ResourceType.conflicts);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.read<ConflictDefinition>(path, ResourceType.user, id, undefined, options);
|
||||
const response = await this.clientContext.read<ConflictDefinition>(path, ResourceType.user, id, options);
|
||||
return new ConflictResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
|
||||
|
@ -48,13 +48,7 @@ export class Conflict {
|
|||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete<ConflictDefinition>(
|
||||
path,
|
||||
ResourceType.conflicts,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.delete<ConflictDefinition>(path, ResourceType.conflicts, id, options);
|
||||
return new ConflictResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -180,13 +180,7 @@ export class Container {
|
|||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.read<ContainerDefinition>(
|
||||
path,
|
||||
ResourceType.container,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.read<ContainerDefinition>(path, ResourceType.container, id, options);
|
||||
this.clientContext.partitionKeyDefinitionCache[this.url] = response.result.partitionKey;
|
||||
return new ContainerResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
|
@ -206,7 +200,6 @@ export class Container {
|
|||
path,
|
||||
ResourceType.container,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
return new ContainerResponse(response.result, response.headers, response.statusCode, this);
|
||||
|
@ -217,13 +210,7 @@ export class Container {
|
|||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete<ContainerDefinition>(
|
||||
path,
|
||||
ResourceType.container,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.delete<ContainerDefinition>(path, ResourceType.container, id, options);
|
||||
return new ContainerResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
|
||||
|
|
|
@ -90,28 +90,22 @@ export class Containers {
|
|||
* @param body Represents the body of the container.
|
||||
* @param options Use to set options like response page size, continuation tokens, etc.
|
||||
*/
|
||||
public async create(body: ContainerRequest, options?: RequestOptions): Promise<ContainerResponse> {
|
||||
public async create(body: ContainerRequest, options: RequestOptions = {}): Promise<ContainerResponse> {
|
||||
const err = {};
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
const path = getPathFromLink(this.database.url, ResourceType.container);
|
||||
const id = getIdFromLink(this.database.url);
|
||||
let initialHeaders: CosmosHeaders;
|
||||
|
||||
if (body.throughput) {
|
||||
initialHeaders = { [Constants.HttpHeaders.OfferThroughput]: body.throughput };
|
||||
options.initialHeaders = Object.assign({}, options.initialHeaders, {
|
||||
[Constants.HttpHeaders.OfferThroughput]: body.throughput
|
||||
});
|
||||
delete body.throughput;
|
||||
}
|
||||
|
||||
const response = await this.clientContext.create<ContainerRequest>(
|
||||
body,
|
||||
path,
|
||||
ResourceType.container,
|
||||
id,
|
||||
initialHeaders,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.create<ContainerRequest>(body, path, ResourceType.container, id, options);
|
||||
const ref = new Container(this.database, response.result.id, this.clientContext);
|
||||
return new ContainerResponse(response.result, response.headers, response.statusCode, ref);
|
||||
}
|
||||
|
|
|
@ -79,13 +79,7 @@ export class Database {
|
|||
public async read(options?: RequestOptions): Promise<DatabaseResponse> {
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
const response = await this.clientContext.read<DatabaseDefinition>(
|
||||
path,
|
||||
ResourceType.database,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.read<DatabaseDefinition>(path, ResourceType.database, id, options);
|
||||
return new DatabaseResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
|
||||
|
@ -93,13 +87,7 @@ export class Database {
|
|||
public async delete(options?: RequestOptions): Promise<DatabaseResponse> {
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
const response = await this.clientContext.delete<DatabaseDefinition>(
|
||||
path,
|
||||
ResourceType.database,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.delete<DatabaseDefinition>(path, ResourceType.database, id, options);
|
||||
return new DatabaseResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,16 +90,16 @@ export class Databases {
|
|||
* @param body The {@link DatabaseDefinition} that represents the {@link Database} to be created.
|
||||
* @param options Use to set options like response page size, continuation tokens, etc.
|
||||
*/
|
||||
public async create(body: DatabaseRequest, options?: RequestOptions): Promise<DatabaseResponse> {
|
||||
public async create(body: DatabaseRequest, options: RequestOptions = {}): Promise<DatabaseResponse> {
|
||||
const err = {};
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
let initialHeaders: CosmosHeaders;
|
||||
|
||||
if (body.throughput) {
|
||||
initialHeaders = { [Constants.HttpHeaders.OfferThroughput]: body.throughput };
|
||||
options.initialHeaders = Object.assign({}, options.initialHeaders, {
|
||||
[Constants.HttpHeaders.OfferThroughput]: body.throughput
|
||||
});
|
||||
delete body.throughput;
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,6 @@ export class Databases {
|
|||
path,
|
||||
ResourceType.database,
|
||||
undefined,
|
||||
initialHeaders,
|
||||
options
|
||||
);
|
||||
const ref = new Database(this.client, body.id, this.clientContext);
|
||||
|
|
|
@ -74,7 +74,7 @@ export class Item {
|
|||
}
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
const response = await this.clientContext.read<T>(path, ResourceType.item, id, undefined, options);
|
||||
const response = await this.clientContext.read<T>(path, ResourceType.item, id, options);
|
||||
|
||||
return new ItemResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ export class Item {
|
|||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.replace<T>(body, path, ResourceType.item, id, undefined, options);
|
||||
const response = await this.clientContext.replace<T>(body, path, ResourceType.item, id, options);
|
||||
return new ItemResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
|
||||
|
@ -144,7 +144,7 @@ export class Item {
|
|||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete<T>(path, ResourceType.item, id, undefined, options);
|
||||
const response = await this.clientContext.delete<T>(path, ResourceType.item, id, options);
|
||||
return new ItemResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -223,7 +223,7 @@ export class Items {
|
|||
const path = getPathFromLink(this.container.url, ResourceType.item);
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
const response = await this.clientContext.create<T>(body, path, ResourceType.item, id, undefined, options);
|
||||
const response = await this.clientContext.create<T>(body, path, ResourceType.item, id, options);
|
||||
|
||||
const ref = new Item(
|
||||
this.container,
|
||||
|
@ -275,8 +275,7 @@ export class Items {
|
|||
const path = getPathFromLink(this.container.url, ResourceType.item);
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
const response = (await this.clientContext.upsert<T>(body, path, ResourceType.item, id, undefined, options)) as T &
|
||||
Resource;
|
||||
const response = (await this.clientContext.upsert<T>(body, path, ResourceType.item, id, options)) as T & Resource;
|
||||
|
||||
const ref = new Item(
|
||||
this.container,
|
||||
|
|
|
@ -33,13 +33,7 @@ export class Offer {
|
|||
* @param options
|
||||
*/
|
||||
public async read(options?: RequestOptions): Promise<OfferResponse> {
|
||||
const response = await this.clientContext.read<OfferDefinition>(
|
||||
this.url,
|
||||
ResourceType.offer,
|
||||
this.id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.read<OfferDefinition>(this.url, ResourceType.offer, this.id, options);
|
||||
return new OfferResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
|
||||
|
@ -58,7 +52,6 @@ export class Offer {
|
|||
this.url,
|
||||
ResourceType.offer,
|
||||
this.id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
return new OfferResponse(response.result, response.headers, response.statusCode, this);
|
||||
|
|
|
@ -37,7 +37,6 @@ export class Permission {
|
|||
path,
|
||||
ResourceType.permission,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
return new PermissionResponse(response.result, response.headers, response.statusCode, this);
|
||||
|
@ -62,7 +61,6 @@ export class Permission {
|
|||
path,
|
||||
ResourceType.permission,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
return new PermissionResponse(response.result, response.headers, response.statusCode, this);
|
||||
|
@ -80,7 +78,6 @@ export class Permission {
|
|||
path,
|
||||
ResourceType.permission,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
return new PermissionResponse(response.result, response.headers, response.statusCode, this);
|
||||
|
|
|
@ -83,7 +83,6 @@ export class Permissions {
|
|||
path,
|
||||
ResourceType.permission,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const ref = new Permission(this.user, response.result.id, this.clientContext);
|
||||
|
@ -110,7 +109,6 @@ export class Permissions {
|
|||
path,
|
||||
ResourceType.permission,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const ref = new Permission(this.user, response.result.id, this.clientContext);
|
||||
|
|
|
@ -36,13 +36,7 @@ export class StoredProcedure {
|
|||
public async read(options?: RequestOptions): Promise<StoredProcedureResponse> {
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
const response = await this.clientContext.read<StoredProcedureDefinition>(
|
||||
path,
|
||||
ResourceType.sproc,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.read<StoredProcedureDefinition>(path, ResourceType.sproc, id, options);
|
||||
return new StoredProcedureResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
|
||||
|
@ -69,7 +63,6 @@ export class StoredProcedure {
|
|||
path,
|
||||
ResourceType.sproc,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
return new StoredProcedureResponse(response.result, response.headers, response.statusCode, this);
|
||||
|
@ -83,13 +76,7 @@ export class StoredProcedure {
|
|||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete<StoredProcedureDefinition>(
|
||||
path,
|
||||
ResourceType.sproc,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.delete<StoredProcedureDefinition>(path, ResourceType.sproc, id, options);
|
||||
return new StoredProcedureResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
|
||||
|
|
|
@ -108,7 +108,6 @@ export class StoredProcedures {
|
|||
path,
|
||||
ResourceType.sproc,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const ref = new StoredProcedure(this.container, response.result.id, this.clientContext);
|
||||
|
@ -143,7 +142,6 @@ export class StoredProcedures {
|
|||
path,
|
||||
ResourceType.sproc,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const ref = new StoredProcedure(this.container, response.result.id, this.clientContext);
|
||||
|
|
|
@ -42,13 +42,7 @@ export class Trigger {
|
|||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.read<TriggerDefinition>(
|
||||
path,
|
||||
ResourceType.trigger,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.read<TriggerDefinition>(path, ResourceType.trigger, id, options);
|
||||
return new TriggerResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
|
||||
|
@ -70,14 +64,7 @@ export class Trigger {
|
|||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.replace<TriggerDefinition>(
|
||||
body,
|
||||
path,
|
||||
ResourceType.trigger,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.replace<TriggerDefinition>(body, path, ResourceType.trigger, id, options);
|
||||
return new TriggerResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
|
||||
|
@ -89,13 +76,7 @@ export class Trigger {
|
|||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete<TriggerDefinition>(
|
||||
path,
|
||||
ResourceType.trigger,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.delete<TriggerDefinition>(path, ResourceType.trigger, id, options);
|
||||
return new TriggerResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,14 +83,7 @@ export class Triggers {
|
|||
const path = getPathFromLink(this.container.url, ResourceType.trigger);
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
const response = await this.clientContext.create<TriggerDefinition>(
|
||||
body,
|
||||
path,
|
||||
ResourceType.trigger,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.create<TriggerDefinition>(body, path, ResourceType.trigger, id, options);
|
||||
const ref = new Trigger(this.container, response.result.id, this.clientContext);
|
||||
return new TriggerResponse(response.result, response.headers, response.statusCode, ref);
|
||||
}
|
||||
|
@ -118,14 +111,7 @@ export class Triggers {
|
|||
const path = getPathFromLink(this.container.url, ResourceType.trigger);
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
const response = await this.clientContext.upsert<TriggerDefinition>(
|
||||
body,
|
||||
path,
|
||||
ResourceType.trigger,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.upsert<TriggerDefinition>(body, path, ResourceType.trigger, id, options);
|
||||
const ref = new Trigger(this.container, response.result.id, this.clientContext);
|
||||
return new TriggerResponse(response.result, response.headers, response.statusCode, ref);
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ export class User {
|
|||
public async read(options?: RequestOptions): Promise<UserResponse> {
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
const response = await this.clientContext.read<UserDefinition>(path, ResourceType.user, id, undefined, options);
|
||||
const response = await this.clientContext.read<UserDefinition>(path, ResourceType.user, id, options);
|
||||
return new UserResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
|
||||
|
@ -74,14 +74,7 @@ export class User {
|
|||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.replace<UserDefinition>(
|
||||
body,
|
||||
path,
|
||||
ResourceType.user,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.replace<UserDefinition>(body, path, ResourceType.user, id, options);
|
||||
return new UserResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
|
||||
|
@ -93,7 +86,7 @@ export class User {
|
|||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete<UserDefinition>(path, ResourceType.user, id, undefined, options);
|
||||
const response = await this.clientContext.delete<UserDefinition>(path, ResourceType.user, id, options);
|
||||
return new UserResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,14 +67,7 @@ export class Users {
|
|||
|
||||
const path = getPathFromLink(this.database.url, ResourceType.user);
|
||||
const id = getIdFromLink(this.database.url);
|
||||
const response = await this.clientContext.create<UserDefinition>(
|
||||
body,
|
||||
path,
|
||||
ResourceType.user,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.create<UserDefinition>(body, path, ResourceType.user, id, options);
|
||||
const ref = new User(this.database, response.result.id, this.clientContext);
|
||||
return new UserResponse(response.result, response.headers, response.statusCode, ref);
|
||||
}
|
||||
|
@ -93,14 +86,7 @@ export class Users {
|
|||
const path = getPathFromLink(this.database.url, ResourceType.user);
|
||||
const id = getIdFromLink(this.database.url);
|
||||
|
||||
const response = await this.clientContext.upsert<UserDefinition>(
|
||||
body,
|
||||
path,
|
||||
ResourceType.user,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.upsert<UserDefinition>(body, path, ResourceType.user, id, options);
|
||||
const ref = new User(this.database, response.result.id, this.clientContext);
|
||||
return new UserResponse(response.result, response.headers, response.statusCode, ref);
|
||||
}
|
||||
|
|
|
@ -42,13 +42,7 @@ export class UserDefinedFunction {
|
|||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.read<UserDefinedFunctionDefinition>(
|
||||
path,
|
||||
ResourceType.udf,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const response = await this.clientContext.read<UserDefinedFunctionDefinition>(path, ResourceType.udf, id, options);
|
||||
return new UserDefinedFunctionResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
|
||||
|
@ -78,7 +72,6 @@ export class UserDefinedFunction {
|
|||
path,
|
||||
ResourceType.udf,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
return new UserDefinedFunctionResponse(response.result, response.headers, response.statusCode, this);
|
||||
|
@ -92,7 +85,7 @@ export class UserDefinedFunction {
|
|||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete(path, ResourceType.udf, id, undefined, options);
|
||||
const response = await this.clientContext.delete(path, ResourceType.udf, id, options);
|
||||
return new UserDefinedFunctionResponse(response.result, response.headers, response.statusCode, this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,7 +90,6 @@ export class UserDefinedFunctions {
|
|||
path,
|
||||
ResourceType.udf,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const ref = new UserDefinedFunction(this.container, response.result.id, this.clientContext);
|
||||
|
@ -126,7 +125,6 @@ export class UserDefinedFunctions {
|
|||
path,
|
||||
ResourceType.udf,
|
||||
id,
|
||||
undefined,
|
||||
options
|
||||
);
|
||||
const ref = new UserDefinedFunction(this.container, response.result.id, this.clientContext);
|
||||
|
|
|
@ -96,18 +96,6 @@ export function getHexaDigit() {
|
|||
return Math.floor(Math.random() * 16).toString(16);
|
||||
}
|
||||
|
||||
export function setIsUpsertHeader(headers: CosmosHeaders) {
|
||||
if (headers === undefined || headers === null) {
|
||||
throw new Error('The "headers" parameter must not be null or undefined');
|
||||
}
|
||||
|
||||
if (typeof headers !== "object") {
|
||||
throw new Error('The "headers" parameter must be an instance of "Object". Actual type is: "string".');
|
||||
}
|
||||
|
||||
headers[Constants.HttpHeaders.IsUpsert] = true;
|
||||
}
|
||||
|
||||
// TODO: replace with well known library?
|
||||
export function generateGuidId() {
|
||||
let id = "";
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
// TODO: Should we remove this?
|
||||
export enum QueryCompatibilityMode {
|
||||
Default = 0,
|
||||
Query = 1,
|
||||
SqlQuery = 2
|
||||
}
|
|
@ -11,7 +11,6 @@ export * from "./PartitionKey";
|
|||
export * from "./PartitionKeyDefinition";
|
||||
export * from "./PartitionKind";
|
||||
export * from "./PermissionMode";
|
||||
export * from "./QueryCompatibilityMode";
|
||||
export * from "./TriggerOperation";
|
||||
export * from "./TriggerType";
|
||||
export * from "./UserDefinedFunctionType";
|
||||
|
|
|
@ -16,7 +16,6 @@ export {
|
|||
PartitionKeyDefinition,
|
||||
PartitionKind,
|
||||
PermissionMode,
|
||||
QueryCompatibilityMode,
|
||||
TriggerOperation,
|
||||
TriggerType,
|
||||
UserDefinedFunctionType
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { CosmosHeaders } from "../index";
|
||||
import { SharedOptions } from "./SharedOptions";
|
||||
|
||||
/**
|
||||
* The feed options and query methods.
|
||||
*/
|
||||
export interface FeedOptions {
|
||||
export interface FeedOptions extends SharedOptions {
|
||||
/** Opaque token for continuing the enumeration. */
|
||||
continuation?: string;
|
||||
/**
|
||||
|
@ -27,12 +27,6 @@ export interface FeedOptions {
|
|||
maxDegreeOfParallelism?: number;
|
||||
/** Max number of items to be returned in the enumeration operation. */
|
||||
maxItemCount?: number;
|
||||
/** Specifies a partition key definition for a particular path in the Azure Cosmos DB database service. */
|
||||
partitionKey?: string;
|
||||
/** Token for use with Session consistency. */
|
||||
sessionToken?: string;
|
||||
/** (Advanced use case) Initial headers to start with when sending requests to Cosmos */
|
||||
initialHeaders?: CosmosHeaders;
|
||||
/** Indicates a change feed request. Must be set to "Incremental feed", or omitted otherwise. */
|
||||
useIncrementalFeed?: boolean;
|
||||
/** Conditions Associated with the request. */
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
import { ClientContext } from "../ClientContext";
|
||||
import { OperationType, ResourceType } from "../common";
|
||||
import { HTTPMethod, OperationType, ResourceType } from "../common";
|
||||
import { Agent } from "../CosmosClientOptions";
|
||||
import { ConnectionPolicy } from "../documents";
|
||||
import { GlobalEndpointManager } from "../globalEndpointManager";
|
||||
import { CosmosHeaders } from "../queryExecutionContext/CosmosHeaders";
|
||||
import { FeedOptions } from "./FeedOptions";
|
||||
import { LocationRouting } from "./LocationRouting";
|
||||
import { RequestOptions } from "./RequestOptions";
|
||||
|
||||
export interface RequestContext {
|
||||
path?: string;
|
||||
|
@ -8,5 +14,15 @@ export interface RequestContext {
|
|||
client?: ClientContext;
|
||||
retryCount?: number;
|
||||
resourceType?: ResourceType;
|
||||
resourceId?: string;
|
||||
locationRouting?: LocationRouting;
|
||||
globalEndpointManager: GlobalEndpointManager;
|
||||
connectionPolicy: ConnectionPolicy;
|
||||
requestAgent: Agent;
|
||||
body?: any;
|
||||
headers?: CosmosHeaders;
|
||||
endpoint?: string;
|
||||
method: HTTPMethod;
|
||||
partitionKeyRangeId?: string;
|
||||
options: FeedOptions | RequestOptions;
|
||||
}
|
||||
|
|
|
@ -1,214 +1,103 @@
|
|||
import { AbortController } from "abort-controller";
|
||||
import { Agent, OutgoingHttpHeaders } from "http";
|
||||
import { RequestOptions } from "https"; // TYPES ONLY
|
||||
import fetch from "node-fetch";
|
||||
import { parse } from "url";
|
||||
import { Constants, HTTPMethod } from "../common/constants";
|
||||
import { ConnectionPolicy } from "../documents";
|
||||
import { GlobalEndpointManager } from "../globalEndpointManager";
|
||||
import { CosmosHeaders } from "../queryExecutionContext/CosmosHeaders";
|
||||
import fetch, { RequestInit, Response } from "node-fetch";
|
||||
import { trimSlashes } from "../common";
|
||||
import { Constants } from "../common/constants";
|
||||
import * as RetryUtility from "../retry/retryUtility";
|
||||
import { ErrorResponse } from "./ErrorResponse";
|
||||
import { bodyFromData } from "./request";
|
||||
import { RequestContext } from "./RequestContext";
|
||||
import { Response } from "./Response";
|
||||
import { Response as CosmosResponse } from "./Response";
|
||||
import { TimeoutError } from "./TimeoutError";
|
||||
|
||||
/** @hidden */
|
||||
export class RequestHandler {
|
||||
public constructor(
|
||||
private globalEndpointManager: GlobalEndpointManager,
|
||||
private connectionPolicy: ConnectionPolicy,
|
||||
private requestAgent: Agent
|
||||
) {}
|
||||
public static async createRequestObjectStub(
|
||||
connectionPolicy: ConnectionPolicy,
|
||||
requestOptions: RequestOptions,
|
||||
body?: any
|
||||
) {
|
||||
let didTimeout: boolean;
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const timeout = setTimeout(() => {
|
||||
didTimeout = true;
|
||||
controller.abort();
|
||||
}, connectionPolicy.requestTimeout);
|
||||
|
||||
let response: any;
|
||||
export async function executeRequest(requestContext: RequestContext) {
|
||||
let didTimeout: boolean;
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const timeout = setTimeout(() => {
|
||||
didTimeout = true;
|
||||
controller.abort();
|
||||
}, requestContext.connectionPolicy.requestTimeout);
|
||||
|
||||
try {
|
||||
// TODO Remove any
|
||||
response = await fetch((requestOptions as any).href + requestOptions.path, {
|
||||
method: requestOptions.method,
|
||||
headers: requestOptions.headers as any,
|
||||
agent: requestOptions.agent,
|
||||
signal,
|
||||
...(body && { body })
|
||||
} as any); // TODO Remove any. Upstream issue https://github.com/lquixada/cross-fetch/issues/42
|
||||
} catch (error) {
|
||||
if (error.name === "AbortError") {
|
||||
if (didTimeout === true) {
|
||||
throw new TimeoutError();
|
||||
}
|
||||
// TODO handle user requested cancellation here
|
||||
let response: Response;
|
||||
|
||||
if (requestContext.body) {
|
||||
requestContext.body = bodyFromData(requestContext.body);
|
||||
}
|
||||
|
||||
try {
|
||||
response = await fetch(trimSlashes(requestContext.endpoint) + requestContext.path, {
|
||||
method: requestContext.method,
|
||||
headers: requestContext.headers as any,
|
||||
agent: requestContext.requestAgent,
|
||||
signal,
|
||||
body: requestContext.body
|
||||
} as RequestInit);
|
||||
} catch (error) {
|
||||
if (error.name === "AbortError") {
|
||||
if (didTimeout === true) {
|
||||
throw new TimeoutError();
|
||||
}
|
||||
throw error;
|
||||
// TODO handle user requested cancellation here
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
clearTimeout(timeout);
|
||||
|
||||
const result = response.status === 204 || response.status === 304 ? null : await response.json();
|
||||
|
||||
const headers = {} as any;
|
||||
response.headers.forEach((value: string, key: string) => {
|
||||
headers[key] = value;
|
||||
});
|
||||
|
||||
if (response.status >= 400) {
|
||||
const errorResponse: ErrorResponse = {
|
||||
code: response.status,
|
||||
// TODO Upstream code expects this as a string.
|
||||
// So after parsing to JSON we convert it back to string if there is an error
|
||||
body: JSON.stringify(result),
|
||||
headers
|
||||
};
|
||||
if (Constants.HttpHeaders.ActivityId in headers) {
|
||||
errorResponse.activityId = headers[Constants.HttpHeaders.ActivityId];
|
||||
}
|
||||
|
||||
clearTimeout(timeout);
|
||||
|
||||
const result = response.status === 204 || response.status === 304 ? null : await response.json();
|
||||
|
||||
const headers = {} as any;
|
||||
response.headers.forEach((value: string, key: string) => {
|
||||
headers[key] = value;
|
||||
});
|
||||
|
||||
if (response.status >= 400) {
|
||||
const errorResponse: ErrorResponse = {
|
||||
code: response.status,
|
||||
// TODO Upstream code expects this as a string.
|
||||
// So after parsing to JSON we convert it back to string if there is an error
|
||||
body: JSON.stringify(result),
|
||||
headers
|
||||
};
|
||||
if (Constants.HttpHeaders.ActivityId in headers) {
|
||||
errorResponse.activityId = headers[Constants.HttpHeaders.ActivityId];
|
||||
}
|
||||
|
||||
if (Constants.HttpHeaders.SubStatus in headers) {
|
||||
errorResponse.substatus = parseInt(headers[Constants.HttpHeaders.SubStatus], 10);
|
||||
}
|
||||
|
||||
if (Constants.HttpHeaders.RetryAfterInMilliseconds in headers) {
|
||||
errorResponse.retryAfterInMilliseconds = parseInt(headers[Constants.HttpHeaders.RetryAfterInMilliseconds], 10);
|
||||
}
|
||||
|
||||
return Promise.reject(errorResponse);
|
||||
}
|
||||
return Promise.resolve({
|
||||
headers,
|
||||
result,
|
||||
statusCode: response.status
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the request object, call the passed callback when the response is retrieved.
|
||||
* @param {object} globalEndpointManager - an instance of GlobalEndpointManager class.
|
||||
* @param {object} connectionPolicy - an instance of ConnectionPolicy that has the connection configs.
|
||||
* @param {object} requestAgent - the https agent used for send request
|
||||
* @param {string} method - the http request method ( 'get', 'post', 'put', .. etc ).
|
||||
* @param {String} hostname - The base url for the endpoint.
|
||||
* @param {string} path - the path of the requesed resource.
|
||||
* @param {Object} data - the request body. It can be either string, buffer, or undefined.
|
||||
* @param {Object} queryParams - query parameters for the request.
|
||||
* @param {Object} headers - specific headers for the request.
|
||||
* @param {function} callback - the callback that will be called when the response is retrieved and processed.
|
||||
*/
|
||||
public static async request(
|
||||
globalEndpointManager: GlobalEndpointManager,
|
||||
connectionPolicy: ConnectionPolicy,
|
||||
requestAgent: Agent,
|
||||
method: HTTPMethod,
|
||||
hostname: string,
|
||||
request: RequestContext,
|
||||
data: string | Buffer,
|
||||
headers: CosmosHeaders
|
||||
): Promise<Response<any>> {
|
||||
// TODO: any
|
||||
const path = (request as { path: string }).path === undefined ? request : (request as { path: string }).path;
|
||||
let body: any; // TODO: any
|
||||
|
||||
if (data) {
|
||||
body = bodyFromData(data);
|
||||
if (!body) {
|
||||
return {
|
||||
result: {
|
||||
message: "parameter data must be a javascript object, string, or Buffer"
|
||||
},
|
||||
headers: undefined
|
||||
};
|
||||
}
|
||||
if (Constants.HttpHeaders.SubStatus in headers) {
|
||||
errorResponse.substatus = parseInt(headers[Constants.HttpHeaders.SubStatus], 10);
|
||||
}
|
||||
|
||||
const requestOptions: RequestOptions = parse(hostname);
|
||||
requestOptions.method = method;
|
||||
requestOptions.path += path;
|
||||
requestOptions.headers = headers as OutgoingHttpHeaders;
|
||||
requestOptions.agent = requestAgent;
|
||||
requestOptions.secureProtocol = "TLSv1_client_method"; // TODO: Should be a constant
|
||||
|
||||
if (connectionPolicy.disableSSLVerification === true) {
|
||||
requestOptions.rejectUnauthorized = false;
|
||||
if (Constants.HttpHeaders.RetryAfterInMilliseconds in headers) {
|
||||
errorResponse.retryAfterInMilliseconds = parseInt(headers[Constants.HttpHeaders.RetryAfterInMilliseconds], 10);
|
||||
}
|
||||
|
||||
return RetryUtility.execute({
|
||||
globalEndpointManager,
|
||||
body,
|
||||
createRequestObjectFunc: this.createRequestObjectStub,
|
||||
connectionPolicy,
|
||||
requestOptions,
|
||||
request
|
||||
});
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
public get(urlString: string, request: RequestContext, headers: CosmosHeaders) {
|
||||
// TODO: any
|
||||
return RequestHandler.request(
|
||||
this.globalEndpointManager,
|
||||
this.connectionPolicy,
|
||||
this.requestAgent,
|
||||
HTTPMethod.get,
|
||||
urlString,
|
||||
request,
|
||||
undefined,
|
||||
headers
|
||||
);
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
public post(urlString: string, request: RequestContext, body: any, headers: CosmosHeaders) {
|
||||
// TODO: any
|
||||
return RequestHandler.request(
|
||||
this.globalEndpointManager,
|
||||
this.connectionPolicy,
|
||||
this.requestAgent,
|
||||
HTTPMethod.post,
|
||||
urlString,
|
||||
request,
|
||||
body,
|
||||
headers
|
||||
);
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
public put(urlString: string, request: RequestContext, body: any, headers: CosmosHeaders) {
|
||||
// TODO: any
|
||||
return RequestHandler.request(
|
||||
this.globalEndpointManager,
|
||||
this.connectionPolicy,
|
||||
this.requestAgent,
|
||||
HTTPMethod.put,
|
||||
urlString,
|
||||
request,
|
||||
body,
|
||||
headers
|
||||
);
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
public delete(urlString: string, request: RequestContext, headers: CosmosHeaders) {
|
||||
return RequestHandler.request(
|
||||
this.globalEndpointManager,
|
||||
this.connectionPolicy,
|
||||
this.requestAgent,
|
||||
HTTPMethod.delete,
|
||||
urlString,
|
||||
request,
|
||||
undefined,
|
||||
headers
|
||||
);
|
||||
throw errorResponse;
|
||||
}
|
||||
return {
|
||||
headers,
|
||||
result,
|
||||
statusCode: response.status
|
||||
};
|
||||
}
|
||||
|
||||
export async function request<T>(requestContext: RequestContext): Promise<CosmosResponse<T>> {
|
||||
const { globalEndpointManager, connectionPolicy, body } = requestContext;
|
||||
|
||||
let parsedBody: any; // TODO: any
|
||||
|
||||
if (body) {
|
||||
parsedBody = bodyFromData(body);
|
||||
if (!body) {
|
||||
throw new Error("parameter data must be a javascript object, string, or Buffer");
|
||||
}
|
||||
}
|
||||
|
||||
return RetryUtility.execute({
|
||||
globalEndpointManager,
|
||||
body: parsedBody,
|
||||
connectionPolicy,
|
||||
requestContext
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { PartitionKey } from "../documents";
|
||||
import { CosmosHeaders } from "../index";
|
||||
import { SharedOptions } from "./SharedOptions";
|
||||
|
||||
/**
|
||||
* Options that can be specified for a requested issued to the Azure Cosmos DB servers.=
|
||||
*/
|
||||
export interface RequestOptions {
|
||||
export interface RequestOptions extends SharedOptions {
|
||||
/** Conditions Associated with the request. */
|
||||
accessCondition?: {
|
||||
/** Conditional HTTP method header type (IfMatch or IfNoneMatch). */
|
||||
|
@ -23,8 +22,6 @@ export interface RequestOptions {
|
|||
enableScriptLogging?: boolean;
|
||||
/** Specifies indexing directives (index, do not index .. etc). */
|
||||
indexingDirective?: string;
|
||||
/** Represents Request Units(RU)/Minute throughput is enabled/disabled for a container. */
|
||||
offerEnableRUPerMinuteThroughput?: boolean;
|
||||
/** The offer throughput provisioned for a container in measurement of Requests-per-Unit. */
|
||||
offerThroughput?: number;
|
||||
/**
|
||||
|
@ -33,8 +30,6 @@ export interface RequestOptions {
|
|||
* This option is only valid when creating a document container.
|
||||
*/
|
||||
offerType?: string;
|
||||
/** Specifies a partition key definition for a particular path in the Azure Cosmos DB database service. */
|
||||
partitionKey?: PartitionKey | PartitionKey[];
|
||||
/** Enables/disables getting document container quota related stats for document container read requests. */
|
||||
populateQuotaInfo?: boolean;
|
||||
/** Indicates what is the post trigger to be invoked after the operation. */
|
||||
|
@ -43,10 +38,6 @@ export interface RequestOptions {
|
|||
preTriggerInclude?: string | string[];
|
||||
/** Expiry time (in seconds) for resource token associated with permission (applicable only for requests on permissions). */
|
||||
resourceTokenExpirySeconds?: number;
|
||||
/** Token for use with Session consistency. */
|
||||
sessionToken?: string;
|
||||
/** (Advanced use case) Initial headers to start with when sending requests to Cosmos */
|
||||
initialHeaders?: CosmosHeaders;
|
||||
/** (Advanced use case) The url to connect to. */
|
||||
urlConnection?: string;
|
||||
/** (Advanced use case) Skip getting info on the parititon key from the container. */
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import { PartitionKey } from "../documents";
|
||||
import { CosmosHeaders } from "../index";
|
||||
import { RequestContext } from "./RequestContext";
|
||||
|
||||
interface BeforeOperationArgs {
|
||||
endpoint: string;
|
||||
request: RequestContext;
|
||||
headers: CosmosHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options that can be specified for a requested issued to the Azure Cosmos DB servers.=
|
||||
*/
|
||||
export interface SharedOptions {
|
||||
/** Specifies a partition key definition for a particular path in the Azure Cosmos DB database service. */
|
||||
partitionKey?: PartitionKey | PartitionKey[];
|
||||
/** Enables/disables getting document container quota related stats for document container read requests. */
|
||||
sessionToken?: string;
|
||||
/** (Advanced use case) Initial headers to start with when sending requests to Cosmos */
|
||||
initialHeaders?: CosmosHeaders;
|
||||
/** (Advanced use case) TODO: Document */
|
||||
beforeOperation?: (args: BeforeOperationArgs) => Promise<BeforeOperationArgs>;
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
export { ErrorResponse } from "./ErrorResponse";
|
||||
export { FeedOptions } from "./FeedOptions";
|
||||
export { RequestHandler } from "./RequestHandler";
|
||||
export { RequestOptions } from "./RequestOptions";
|
||||
export { Response } from "./Response";
|
||||
export { ResourceResponse } from "./ResourceResponse";
|
||||
|
|
|
@ -24,112 +24,122 @@ export function bodyFromData(data: Buffer | string | object) {
|
|||
return data;
|
||||
}
|
||||
|
||||
export async function getHeaders(
|
||||
authOptions: AuthOptions,
|
||||
defaultHeaders: CosmosHeaders,
|
||||
verb: HTTPMethod,
|
||||
path: string,
|
||||
resourceId: string,
|
||||
resourceType: ResourceType,
|
||||
options: RequestOptions | FeedOptions,
|
||||
partitionKeyRangeId?: string,
|
||||
useMultipleWriteLocations?: boolean
|
||||
): Promise<CosmosHeaders> {
|
||||
interface GetHeadersOptions {
|
||||
authOptions: AuthOptions;
|
||||
defaultHeaders: CosmosHeaders;
|
||||
verb: HTTPMethod;
|
||||
path: string;
|
||||
resourceId: string;
|
||||
resourceType: ResourceType;
|
||||
options: RequestOptions & FeedOptions;
|
||||
partitionKeyRangeId?: string;
|
||||
useMultipleWriteLocations?: boolean;
|
||||
}
|
||||
|
||||
export async function getHeaders({
|
||||
authOptions,
|
||||
defaultHeaders,
|
||||
verb,
|
||||
path,
|
||||
resourceId,
|
||||
resourceType,
|
||||
options,
|
||||
partitionKeyRangeId,
|
||||
useMultipleWriteLocations
|
||||
}: GetHeadersOptions): Promise<CosmosHeaders> {
|
||||
const headers: CosmosHeaders = { ...defaultHeaders };
|
||||
const opts: RequestOptions & FeedOptions = (options || {}) as any; // TODO: this is dirty
|
||||
|
||||
if (useMultipleWriteLocations) {
|
||||
headers[Constants.HttpHeaders.ALLOW_MULTIPLE_WRITES] = true;
|
||||
}
|
||||
|
||||
if (opts.continuation) {
|
||||
headers[Constants.HttpHeaders.Continuation] = opts.continuation;
|
||||
if (options.continuation) {
|
||||
headers[Constants.HttpHeaders.Continuation] = options.continuation;
|
||||
}
|
||||
|
||||
if (opts.preTriggerInclude) {
|
||||
if (options.preTriggerInclude) {
|
||||
headers[Constants.HttpHeaders.PreTriggerInclude] =
|
||||
opts.preTriggerInclude.constructor === Array
|
||||
? (opts.preTriggerInclude as string[]).join(",")
|
||||
: (opts.preTriggerInclude as string);
|
||||
options.preTriggerInclude.constructor === Array
|
||||
? (options.preTriggerInclude as string[]).join(",")
|
||||
: (options.preTriggerInclude as string);
|
||||
}
|
||||
|
||||
if (opts.postTriggerInclude) {
|
||||
if (options.postTriggerInclude) {
|
||||
headers[Constants.HttpHeaders.PostTriggerInclude] =
|
||||
opts.postTriggerInclude.constructor === Array
|
||||
? (opts.postTriggerInclude as string[]).join(",")
|
||||
: (opts.postTriggerInclude as string);
|
||||
options.postTriggerInclude.constructor === Array
|
||||
? (options.postTriggerInclude as string[]).join(",")
|
||||
: (options.postTriggerInclude as string);
|
||||
}
|
||||
|
||||
if (opts.offerType) {
|
||||
headers[Constants.HttpHeaders.OfferType] = opts.offerType;
|
||||
if (options.offerType) {
|
||||
headers[Constants.HttpHeaders.OfferType] = options.offerType;
|
||||
}
|
||||
|
||||
if (opts.offerThroughput) {
|
||||
headers[Constants.HttpHeaders.OfferThroughput] = opts.offerThroughput;
|
||||
if (options.offerThroughput) {
|
||||
headers[Constants.HttpHeaders.OfferThroughput] = options.offerThroughput;
|
||||
}
|
||||
|
||||
if (opts.maxItemCount) {
|
||||
headers[Constants.HttpHeaders.PageSize] = opts.maxItemCount;
|
||||
if (options.maxItemCount) {
|
||||
headers[Constants.HttpHeaders.PageSize] = options.maxItemCount;
|
||||
}
|
||||
|
||||
if (opts.accessCondition) {
|
||||
if (opts.accessCondition.type === "IfMatch") {
|
||||
headers[Constants.HttpHeaders.IfMatch] = opts.accessCondition.condition;
|
||||
if (options.accessCondition) {
|
||||
if (options.accessCondition.type === "IfMatch") {
|
||||
headers[Constants.HttpHeaders.IfMatch] = options.accessCondition.condition;
|
||||
} else {
|
||||
headers[Constants.HttpHeaders.IfNoneMatch] = opts.accessCondition.condition;
|
||||
headers[Constants.HttpHeaders.IfNoneMatch] = options.accessCondition.condition;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.useIncrementalFeed) {
|
||||
if (options.useIncrementalFeed) {
|
||||
headers[Constants.HttpHeaders.A_IM] = "Incremental Feed";
|
||||
}
|
||||
|
||||
if (opts.indexingDirective) {
|
||||
headers[Constants.HttpHeaders.IndexingDirective] = opts.indexingDirective;
|
||||
if (options.indexingDirective) {
|
||||
headers[Constants.HttpHeaders.IndexingDirective] = options.indexingDirective;
|
||||
}
|
||||
|
||||
if (opts.consistencyLevel) {
|
||||
headers[Constants.HttpHeaders.ConsistencyLevel] = opts.consistencyLevel;
|
||||
if (options.consistencyLevel) {
|
||||
headers[Constants.HttpHeaders.ConsistencyLevel] = options.consistencyLevel;
|
||||
}
|
||||
|
||||
if (opts.resourceTokenExpirySeconds) {
|
||||
headers[Constants.HttpHeaders.ResourceTokenExpiry] = opts.resourceTokenExpirySeconds;
|
||||
if (options.resourceTokenExpirySeconds) {
|
||||
headers[Constants.HttpHeaders.ResourceTokenExpiry] = options.resourceTokenExpirySeconds;
|
||||
}
|
||||
|
||||
if (opts.sessionToken) {
|
||||
headers[Constants.HttpHeaders.SessionToken] = opts.sessionToken;
|
||||
if (options.sessionToken) {
|
||||
headers[Constants.HttpHeaders.SessionToken] = options.sessionToken;
|
||||
}
|
||||
|
||||
if (opts.enableScanInQuery) {
|
||||
headers[Constants.HttpHeaders.EnableScanInQuery] = opts.enableScanInQuery;
|
||||
if (options.enableScanInQuery) {
|
||||
headers[Constants.HttpHeaders.EnableScanInQuery] = options.enableScanInQuery;
|
||||
}
|
||||
|
||||
if (opts.enableCrossPartitionQuery) {
|
||||
headers[Constants.HttpHeaders.EnableCrossPartitionQuery] = opts.enableCrossPartitionQuery;
|
||||
if (options.enableCrossPartitionQuery) {
|
||||
headers[Constants.HttpHeaders.EnableCrossPartitionQuery] = options.enableCrossPartitionQuery;
|
||||
}
|
||||
|
||||
if (opts.populateQuotaInfo) {
|
||||
headers[Constants.HttpHeaders.PopulateQuotaInfo] = opts.populateQuotaInfo;
|
||||
if (options.populateQuotaInfo) {
|
||||
headers[Constants.HttpHeaders.PopulateQuotaInfo] = options.populateQuotaInfo;
|
||||
}
|
||||
|
||||
if (opts.populateQueryMetrics) {
|
||||
headers[Constants.HttpHeaders.PopulateQueryMetrics] = opts.populateQueryMetrics;
|
||||
if (options.populateQueryMetrics) {
|
||||
headers[Constants.HttpHeaders.PopulateQueryMetrics] = options.populateQueryMetrics;
|
||||
}
|
||||
|
||||
if (opts.maxDegreeOfParallelism !== undefined) {
|
||||
if (options.maxDegreeOfParallelism !== undefined) {
|
||||
headers[Constants.HttpHeaders.ParallelizeCrossPartitionQuery] = true;
|
||||
}
|
||||
|
||||
if (opts.populateQuotaInfo) {
|
||||
if (options.populateQuotaInfo) {
|
||||
headers[Constants.HttpHeaders.PopulateQuotaInfo] = true;
|
||||
}
|
||||
|
||||
if (opts.partitionKey !== undefined) {
|
||||
let partitionKey: string[] | string = opts.partitionKey;
|
||||
if (partitionKey === null || !Array.isArray(partitionKey)) {
|
||||
partitionKey = [partitionKey as string];
|
||||
if (options.partitionKey !== undefined) {
|
||||
if (options.partitionKey === null || !Array.isArray(options.partitionKey)) {
|
||||
options.partitionKey = [options.partitionKey as string];
|
||||
}
|
||||
headers[Constants.HttpHeaders.PartitionKey] = jsonStringifyAndEscapeNonASCII(partitionKey);
|
||||
headers[Constants.HttpHeaders.PartitionKey] = jsonStringifyAndEscapeNonASCII(options.partitionKey);
|
||||
}
|
||||
|
||||
if (authOptions.masterKey || authOptions.key || authOptions.tokenProvider) {
|
||||
|
@ -150,15 +160,11 @@ export async function getHeaders(
|
|||
headers[Constants.HttpHeaders.PartitionKeyRangeID] = partitionKeyRangeId;
|
||||
}
|
||||
|
||||
if (opts.enableScriptLogging) {
|
||||
headers[Constants.HttpHeaders.EnableScriptLogging] = opts.enableScriptLogging;
|
||||
if (options.enableScriptLogging) {
|
||||
headers[Constants.HttpHeaders.EnableScriptLogging] = options.enableScriptLogging;
|
||||
}
|
||||
|
||||
if (opts.offerEnableRUPerMinuteThroughput) {
|
||||
headers[Constants.HttpHeaders.OfferIsRUPerMinuteThroughputEnabled] = true;
|
||||
}
|
||||
|
||||
if (opts.disableRUPerMinuteUsage) {
|
||||
if (options.disableRUPerMinuteUsage) {
|
||||
headers[Constants.HttpHeaders.DisableRUPerMinuteUsage] = true;
|
||||
}
|
||||
if (
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { RequestOptions } from "https";
|
||||
import * as url from "url";
|
||||
import { Constants } from "../common/constants";
|
||||
import { sleep } from "../common/helper";
|
||||
import { StatusCodes, SubStatusCodes } from "../common/statusCodes";
|
||||
|
@ -8,6 +7,7 @@ import { GlobalEndpointManager } from "../globalEndpointManager";
|
|||
import { Response } from "../request";
|
||||
import { LocationRouting } from "../request/LocationRouting";
|
||||
import { RequestContext } from "../request/RequestContext";
|
||||
import { executeRequest } from "../request/RequestHandler";
|
||||
import { DefaultRetryPolicy } from "./defaultRetryPolicy";
|
||||
import { EndpointDiscoveryRetryPolicy } from "./endpointDiscoveryRetryPolicy";
|
||||
import { ResourceThrottleRetryPolicy } from "./resourceThrottleRetryPolicy";
|
||||
|
@ -25,12 +25,10 @@ export type CreateRequestObjectStubFunction = (
|
|||
interface ExecuteArgs {
|
||||
globalEndpointManager: GlobalEndpointManager;
|
||||
body: Buffer;
|
||||
createRequestObjectFunc: CreateRequestObjectStubFunction;
|
||||
connectionPolicy: ConnectionPolicy;
|
||||
requestOptions: RequestOptions;
|
||||
request: RequestContext;
|
||||
retryContext?: RetryContext;
|
||||
retryPolicies?: RetryPolicies;
|
||||
requestContext: RequestContext;
|
||||
}
|
||||
|
||||
interface RetryPolicies {
|
||||
|
@ -42,13 +40,11 @@ interface RetryPolicies {
|
|||
|
||||
export async function execute({
|
||||
body,
|
||||
createRequestObjectFunc,
|
||||
connectionPolicy,
|
||||
requestOptions,
|
||||
globalEndpointManager,
|
||||
request,
|
||||
retryContext,
|
||||
retryPolicies
|
||||
retryPolicies,
|
||||
requestContext
|
||||
}: ExecuteArgs): Promise<Response<any>> {
|
||||
// TODO: any response
|
||||
|
||||
|
@ -57,7 +53,10 @@ export async function execute({
|
|||
}
|
||||
if (!retryPolicies) {
|
||||
retryPolicies = {
|
||||
endpointDiscoveryRetryPolicy: new EndpointDiscoveryRetryPolicy(globalEndpointManager, request.operationType),
|
||||
endpointDiscoveryRetryPolicy: new EndpointDiscoveryRetryPolicy(
|
||||
globalEndpointManager,
|
||||
requestContext.operationType
|
||||
),
|
||||
resourceThrottleRetryPolicy: new ResourceThrottleRetryPolicy(
|
||||
connectionPolicy.retryOptions.maxRetryAttemptCount,
|
||||
connectionPolicy.retryOptions.fixedRetryIntervalInMilliseconds,
|
||||
|
@ -65,30 +64,30 @@ export async function execute({
|
|||
),
|
||||
sessionReadRetryPolicy: new SessionRetryPolicy(
|
||||
globalEndpointManager,
|
||||
request.resourceType,
|
||||
request.operationType,
|
||||
requestContext.resourceType,
|
||||
requestContext.operationType,
|
||||
connectionPolicy
|
||||
),
|
||||
defaultRetryPolicy: new DefaultRetryPolicy(request.operationType)
|
||||
defaultRetryPolicy: new DefaultRetryPolicy(requestContext.operationType)
|
||||
};
|
||||
}
|
||||
const httpsRequest = createRequestObjectFunc(connectionPolicy, requestOptions, body);
|
||||
if (!request.locationRouting) {
|
||||
request.locationRouting = new LocationRouting();
|
||||
const httpsRequest = executeRequest(requestContext);
|
||||
if (!requestContext.locationRouting) {
|
||||
requestContext.locationRouting = new LocationRouting();
|
||||
}
|
||||
request.locationRouting.clearRouteToLocation();
|
||||
requestContext.locationRouting.clearRouteToLocation();
|
||||
if (retryContext) {
|
||||
request.locationRouting.routeToLocation(
|
||||
requestContext.locationRouting.routeToLocation(
|
||||
retryContext.retryCount || 0,
|
||||
!retryContext.retryRequestOnPreferredLocations
|
||||
);
|
||||
if (retryContext.clearSessionTokenNotAvailable) {
|
||||
request.client.clearSessionToken(request.path);
|
||||
requestContext.client.clearSessionToken(requestContext.path);
|
||||
}
|
||||
}
|
||||
const locationEndpoint = await globalEndpointManager.resolveServiceEndpoint(request);
|
||||
requestOptions = modifyRequestOptions(requestOptions, url.parse(locationEndpoint));
|
||||
request.locationRouting.routeToLocation(locationEndpoint);
|
||||
const locationEndpoint = await globalEndpointManager.resolveServiceEndpoint(requestContext);
|
||||
requestContext.endpoint = locationEndpoint;
|
||||
requestContext.locationRouting.routeToLocation(locationEndpoint);
|
||||
try {
|
||||
const response = await (httpsRequest as Promise<Response<any>>);
|
||||
response.headers[Constants.ThrottleRetryCount] = retryPolicies.resourceThrottleRetryPolicy.currentRetryAttemptCount;
|
||||
|
@ -116,36 +115,20 @@ export async function execute({
|
|||
err.headers = { ...err.headers, ...headers };
|
||||
throw err;
|
||||
} else {
|
||||
request.retryCount++;
|
||||
requestContext.retryCount++;
|
||||
const newUrl = (results as any)[1]; // TODO: any hack
|
||||
if (newUrl !== undefined) {
|
||||
modifyRequestOptions(requestOptions, url.parse(newUrl));
|
||||
requestContext.endpoint = newUrl;
|
||||
}
|
||||
await sleep(retryPolicy.retryAfterInMilliseconds);
|
||||
return execute({
|
||||
body,
|
||||
createRequestObjectFunc,
|
||||
connectionPolicy,
|
||||
requestOptions,
|
||||
globalEndpointManager,
|
||||
request,
|
||||
requestContext,
|
||||
retryContext,
|
||||
retryPolicies
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function modifyRequestOptions(
|
||||
oldRequestOptions: RequestOptions | any, // TODO: any hack is bad
|
||||
newUrl: url.UrlWithStringQuery | any
|
||||
) {
|
||||
// TODO: any hack is bad
|
||||
const properties = Object.keys(newUrl);
|
||||
for (const index in properties) {
|
||||
if (properties[index] !== "path") {
|
||||
oldRequestOptions[properties[index]] = newUrl[properties[index]];
|
||||
}
|
||||
}
|
||||
return oldRequestOptions;
|
||||
}
|
||||
|
|
|
@ -13,17 +13,13 @@ describe("NodeJS CRUD Tests", function() {
|
|||
});
|
||||
|
||||
describe("validate database account functionality", function() {
|
||||
const databaseAccountTest = async function() {
|
||||
it("nativeApi Should get database account successfully name based", async function() {
|
||||
const { resource: databaseAccount, headers } = await client.getDatabaseAccount();
|
||||
assert.equal(databaseAccount.DatabasesLink, "/dbs/");
|
||||
assert.equal(databaseAccount.MediaLink, "/media/");
|
||||
assert.equal(databaseAccount.MaxMediaStorageUsageInMB, headers["x-ms-max-media-storage-usage-mb"]); // TODO: should use constants here
|
||||
assert.equal(databaseAccount.CurrentMediaStorageUsageInMB, headers["x-ms-media-storage-usage-mb"]);
|
||||
assert(databaseAccount.ConsistencyPolicy !== undefined);
|
||||
};
|
||||
|
||||
it("nativeApi Should get database account successfully name based", async function() {
|
||||
await databaseAccountTest();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,11 +4,12 @@ import { ClientContext } from "../../dist-esm/ClientContext";
|
|||
import { OperationType, ResourceType, trimSlashes } from "../../dist-esm/common";
|
||||
import { ConsistencyLevel, PartitionKind } from "../../dist-esm/documents";
|
||||
import { Constants, CosmosClient, CosmosHeaders } from "../../dist-esm/index";
|
||||
import { RequestHandler } from "../../dist-esm/request";
|
||||
import { SessionContainer } from "../../dist-esm/session/sessionContainer";
|
||||
import { VectorSessionToken } from "../../dist-esm/session/VectorSessionToken";
|
||||
import { endpoint, masterKey } from "../common/_testConfig";
|
||||
import { getTestDatabase, removeAllDatabases } from "../common/TestHelpers";
|
||||
import * as RequestHandler from "../../dist-esm/request/RequestHandler";
|
||||
import { RequestContext } from "../../dist-esm/request/RequestContext";
|
||||
|
||||
// TODO: there is alot of "any" types for tokens here
|
||||
// TODO: there is alot of leaky document client stuff here that will make removing document client hard
|
||||
|
@ -35,13 +36,9 @@ describe("Session Token", function() {
|
|||
const containerOptions = { offerThroughput: 25100 };
|
||||
|
||||
const clientContext: ClientContext = (client as any).clientContext;
|
||||
const requestHandler: RequestHandler = (clientContext as any).requestHandler;
|
||||
const sessionContainer: SessionContainer = (clientContext as any).sessionContainer;
|
||||
|
||||
const getSpy = sinon.spy(requestHandler, "get");
|
||||
const postSpy = sinon.spy(requestHandler, "post");
|
||||
const putSpy = sinon.spy(requestHandler, "put");
|
||||
const deleteSpy = sinon.spy(requestHandler, "delete");
|
||||
const spy = sinon.spy(RequestHandler, "request");
|
||||
|
||||
beforeEach(async function() {
|
||||
await removeAllDatabases();
|
||||
|
@ -52,7 +49,7 @@ describe("Session Token", function() {
|
|||
|
||||
const { resource: createdContainerDef } = await database.containers.create(containerDefinition, containerOptions);
|
||||
const container = database.container(createdContainerDef.id);
|
||||
assert.equal(postSpy.lastCall.args[3][Constants.HttpHeaders.SessionToken], undefined);
|
||||
assert.equal(spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken], undefined);
|
||||
// TODO: testing implementation detail by looking at containerResourceIdToSesssionTokens
|
||||
let collRid2SessionToken: Map<string, Map<string, VectorSessionToken>> = (sessionContainer as any)
|
||||
.collectionResourceIdToSessionTokens;
|
||||
|
@ -60,7 +57,7 @@ describe("Session Token", function() {
|
|||
|
||||
const { resource: document1 } = await container.items.create({ id: "1" });
|
||||
assert.equal(
|
||||
postSpy.lastCall.args[3][Constants.HttpHeaders.SessionToken],
|
||||
spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken],
|
||||
undefined,
|
||||
"Initial create token should be qual"
|
||||
);
|
||||
|
@ -82,7 +79,11 @@ describe("Session Token", function() {
|
|||
resourceId: "2"
|
||||
});
|
||||
const { resource: document2 } = await container.items.create({ id: "2" });
|
||||
assert.equal(postSpy.lastCall.args[3][Constants.HttpHeaders.SessionToken], token, "create token should be equal");
|
||||
assert.equal(
|
||||
spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken],
|
||||
token,
|
||||
"create token should be equal"
|
||||
);
|
||||
|
||||
collRid2SessionToken = getCollection2TokenMap(sessionContainer);
|
||||
assert.equal(collRid2SessionToken.size, 1, "Should only have one container in the sessioncontainer");
|
||||
|
@ -107,7 +108,11 @@ describe("Session Token", function() {
|
|||
resourceId: "1"
|
||||
});
|
||||
await container.item(document1.id, "1").read();
|
||||
assert.equal(getSpy.lastCall.args[2][Constants.HttpHeaders.SessionToken], readToken, "read token should be equal");
|
||||
assert.equal(
|
||||
spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken],
|
||||
readToken,
|
||||
"read token should be equal"
|
||||
);
|
||||
|
||||
collRid2SessionToken = getCollection2TokenMap(sessionContainer);
|
||||
assert.equal(collRid2SessionToken.size, 1, "Should only have one container in the sessioncontainer");
|
||||
|
@ -136,7 +141,7 @@ describe("Session Token", function() {
|
|||
{ partitionKey: "1" }
|
||||
);
|
||||
assert.equal(
|
||||
postSpy.lastCall.args[3][Constants.HttpHeaders.SessionToken],
|
||||
spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken],
|
||||
upsertToken,
|
||||
"upsert token should be equal"
|
||||
);
|
||||
|
@ -167,7 +172,7 @@ describe("Session Token", function() {
|
|||
});
|
||||
await container.item(document2.id, "2").delete();
|
||||
assert.equal(
|
||||
deleteSpy.lastCall.args[2][Constants.HttpHeaders.SessionToken],
|
||||
spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken],
|
||||
deleteToken,
|
||||
"delete token should be equal"
|
||||
);
|
||||
|
@ -198,7 +203,7 @@ describe("Session Token", function() {
|
|||
});
|
||||
await container.item(document13.id).replace({ id: "1", operation: "replace" }, { partitionKey: "1" });
|
||||
assert.equal(
|
||||
putSpy.lastCall.args[3][Constants.HttpHeaders.SessionToken],
|
||||
spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken],
|
||||
replaceToken,
|
||||
"replace token should be equal"
|
||||
);
|
||||
|
@ -230,7 +235,7 @@ describe("Session Token", function() {
|
|||
resourceType: ResourceType.item
|
||||
});
|
||||
await queryIterator.fetchAll();
|
||||
assert.equal(postSpy.lastCall.args[3][Constants.HttpHeaders.SessionToken], queryToken);
|
||||
assert.equal(spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken], queryToken);
|
||||
|
||||
collRid2SessionToken = getCollection2TokenMap(sessionContainer);
|
||||
assert.equal(collRid2SessionToken.size, 1, "Should only have one container in the sessioncontainer");
|
||||
|
@ -256,17 +261,14 @@ describe("Session Token", function() {
|
|||
});
|
||||
await container.delete();
|
||||
assert.equal(
|
||||
deleteSpy.lastCall.args[2][Constants.HttpHeaders.SessionToken],
|
||||
spy.lastCall.args[0].headers[Constants.HttpHeaders.SessionToken],
|
||||
deleteContainerToken,
|
||||
"delete container token should match"
|
||||
);
|
||||
collRid2SessionToken = getCollection2TokenMap(sessionContainer);
|
||||
assert.equal(collRid2SessionToken.size, 0, "collRid map should be empty on container delete");
|
||||
|
||||
getSpy.restore();
|
||||
postSpy.restore();
|
||||
deleteSpy.restore();
|
||||
putSpy.restore();
|
||||
spy.restore();
|
||||
});
|
||||
|
||||
it("validate 'lsn not caught up' error for higher lsn and clearing session token", async function() {
|
||||
|
@ -287,19 +289,19 @@ describe("Session Token", function() {
|
|||
|
||||
await database.containers.create(containerDefinition, containerOptions);
|
||||
const container = database.container(containerDefinition.id);
|
||||
const { headers } = await container.items.create({ id: "1" });
|
||||
const callbackSpy = sinon.spy(function(path: string, reqHeaders: CosmosHeaders) {
|
||||
await container.items.create({ id: "1" });
|
||||
const callbackSpy = sinon.spy(function(requestContext: RequestContext) {
|
||||
const oldTokens = getCollection2TokenMap(sessionContainer);
|
||||
reqHeaders[Constants.HttpHeaders.SessionToken] = increaseLSN(oldTokens);
|
||||
requestContext.headers[Constants.HttpHeaders.SessionToken] = increaseLSN(oldTokens);
|
||||
});
|
||||
const applySessionTokenStub = sinon.stub(clientContext as any, "applySessionToken").callsFake(callbackSpy);
|
||||
const applySessionTokenStub = sinon.stub(clientContext as any, "applySessionToken").callsFake(callbackSpy as any);
|
||||
try {
|
||||
await container.item("1").read({ partitionKey: "1" });
|
||||
assert.fail("readDocument must throw");
|
||||
} catch (err) {
|
||||
assert.equal(err.substatus, 1002, "Substatus should indicate the LSN didn't catchup.");
|
||||
assert.equal(callbackSpy.callCount, 1);
|
||||
assert.equal(trimSlashes(callbackSpy.lastCall.args[0]), containerLink + "/docs/1");
|
||||
assert.equal(trimSlashes(callbackSpy.lastCall.args[0].path), containerLink + "/docs/1");
|
||||
} finally {
|
||||
applySessionTokenStub.restore();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import assert from "assert";
|
||||
import { CosmosHeaders } from "../../dist-esm";
|
||||
import { Constants, isResourceValid, setIsUpsertHeader } from "../../dist-esm/common";
|
||||
import { isResourceValid } from "../../dist-esm/common";
|
||||
|
||||
describe("Helper methods", function() {
|
||||
describe("isResourceValid Unit Tests", function() {
|
||||
|
@ -13,37 +12,4 @@ describe("Helper methods", function() {
|
|||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe("setIsUpsertHeader", function() {
|
||||
it("Should add is-upsert header.", function(done) {
|
||||
const headers: any = {};
|
||||
assert.equal(undefined, headers[Constants.HttpHeaders.IsUpsert]);
|
||||
setIsUpsertHeader(headers);
|
||||
assert.equal(true, headers[Constants.HttpHeaders.IsUpsert]);
|
||||
done();
|
||||
});
|
||||
|
||||
it("Should update is-upsert header.", function(done) {
|
||||
const headers: CosmosHeaders = {};
|
||||
headers[Constants.HttpHeaders.IsUpsert] = false;
|
||||
assert.equal(false, headers[Constants.HttpHeaders.IsUpsert]);
|
||||
setIsUpsertHeader(headers);
|
||||
assert.equal(true, headers[Constants.HttpHeaders.IsUpsert]);
|
||||
done();
|
||||
});
|
||||
|
||||
it("Should throw on undefined headers", function(done) {
|
||||
assert.throws(function() {
|
||||
setIsUpsertHeader(undefined);
|
||||
}, /The "headers" parameter must not be null or undefined/);
|
||||
done();
|
||||
});
|
||||
|
||||
it("Should throw on null headers", function(done) {
|
||||
assert.throws(function() {
|
||||
setIsUpsertHeader(null);
|
||||
}, /The "headers" parameter must not be null or undefined/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@ import { LocationCache } from "../../dist-esm/LocationCache";
|
|||
|
||||
import * as assert from "assert";
|
||||
import { OperationType, ResourceType } from "../../dist-esm/common";
|
||||
import { RequestContext } from "../../dist-esm/request/RequestContext";
|
||||
|
||||
const scenarios: Scenario[] = [];
|
||||
const regions = ["westus", "East US", "eastus2", "south Centralus", "sEasIa"];
|
||||
|
|
Загрузка…
Ссылка в новой задаче