Родитель
fffca68542
Коммит
5526cb131f
|
@ -1,6 +1,14 @@
|
|||
import { PartitionKeyRange } from "./client/Container/PartitionKeyRange";
|
||||
import { Resource } from "./client/Resource";
|
||||
import { Helper, StatusCodes, SubStatusCodes } from "./common";
|
||||
import {
|
||||
getIdFromLink,
|
||||
getPathFromLink,
|
||||
parseConnectionPolicy,
|
||||
parseLink,
|
||||
setIsUpsertHeader,
|
||||
StatusCodes,
|
||||
SubStatusCodes
|
||||
} from "./common";
|
||||
import { ConnectionPolicy, ConsistencyLevel, DatabaseAccount, QueryCompatibilityMode } from "./documents";
|
||||
import { GlobalEndpointManager } from "./globalEndpointManager";
|
||||
import {
|
||||
|
@ -33,7 +41,7 @@ export class ClientContext {
|
|||
private cosmosClientOptions: CosmosClientOptions,
|
||||
private globalEndpointManager: GlobalEndpointManager
|
||||
) {
|
||||
this.connectionPolicy = Helper.parseConnectionPolicy(cosmosClientOptions.connectionPolicy);
|
||||
this.connectionPolicy = parseConnectionPolicy(cosmosClientOptions.connectionPolicy);
|
||||
this.sessionContainer = new SessionContainer();
|
||||
this.requestHandler = new RequestHandler(
|
||||
globalEndpointManager,
|
||||
|
@ -159,8 +167,8 @@ export class ClientContext {
|
|||
}
|
||||
|
||||
public queryPartitionKeyRanges(collectionLink: string, query?: string | SqlQuerySpec, options?: FeedOptions) {
|
||||
const path = Helper.getPathFromLink(collectionLink, "pkranges");
|
||||
const id = Helper.getIdFromLink(collectionLink);
|
||||
const path = getPathFromLink(collectionLink, "pkranges");
|
||||
const id = getIdFromLink(collectionLink);
|
||||
const cb: FetchFunctionCallback = innerOptions => {
|
||||
return this.queryFeed(path, "pkranges", id, result => result.PartitionKeyRanges, query, innerOptions);
|
||||
};
|
||||
|
@ -198,7 +206,7 @@ export class ClientContext {
|
|||
// 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);
|
||||
if (Helper.parseLink(path).type !== "colls") {
|
||||
if (parseLink(path).type !== "colls") {
|
||||
this.captureSessionToken(undefined, path, Constants.OperationTypes.Delete, response.headers);
|
||||
} else {
|
||||
this.clearSessionToken(path);
|
||||
|
@ -392,7 +400,7 @@ export class ClientContext {
|
|||
resourceType: type
|
||||
};
|
||||
|
||||
Helper.setIsUpsertHeader(requestHeaders);
|
||||
setIsUpsertHeader(requestHeaders);
|
||||
this.applySessionToken(path, requestHeaders);
|
||||
|
||||
// upsert will use WriteEndpoint since it uses POST operation
|
||||
|
@ -418,8 +426,8 @@ export class ClientContext {
|
|||
if (params !== null && params !== undefined && !Array.isArray(params)) {
|
||||
params = [params];
|
||||
}
|
||||
const path = Helper.getPathFromLink(sprocLink);
|
||||
const id = Helper.getIdFromLink(sprocLink);
|
||||
const path = getPathFromLink(sprocLink);
|
||||
const id = getIdFromLink(sprocLink);
|
||||
|
||||
const headers = await getHeaders(
|
||||
this.cosmosClientOptions.auth,
|
||||
|
@ -507,7 +515,7 @@ export class ClientContext {
|
|||
throw new Error("collectionLink cannot be null");
|
||||
}
|
||||
|
||||
const paths = Helper.parseLink(collectionLink);
|
||||
const paths = parseLink(collectionLink);
|
||||
|
||||
if (paths === undefined) {
|
||||
return "";
|
||||
|
@ -525,7 +533,7 @@ export class ClientContext {
|
|||
private getSessionParams(resourceLink: string): SessionContext {
|
||||
const resourceId: string = null;
|
||||
let resourceAddress: string = null;
|
||||
const parserOutput = Helper.parseLink(resourceLink);
|
||||
const parserOutput = parseLink(resourceLink);
|
||||
|
||||
resourceAddress = parserOutput.objectBody.self;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as url from "url";
|
|||
import { Database, Databases } from "./client/Database";
|
||||
import { Offer, Offers } from "./client/Offer";
|
||||
import { ClientContext } from "./ClientContext";
|
||||
import { Constants, Helper, Platform } from "./common";
|
||||
import { Constants, getPlatformDefaultHeaders, getUserAgent, parseConnectionPolicy } from "./common";
|
||||
import { CosmosClientOptions } from "./CosmosClientOptions";
|
||||
import { DatabaseAccount } from "./documents";
|
||||
import { GlobalEndpointManager } from "./globalEndpointManager";
|
||||
|
@ -59,7 +59,7 @@ export class CosmosClient {
|
|||
options.auth.key = options.key;
|
||||
}
|
||||
|
||||
options.connectionPolicy = Helper.parseConnectionPolicy(options.connectionPolicy);
|
||||
options.connectionPolicy = parseConnectionPolicy(options.connectionPolicy);
|
||||
|
||||
options.defaultHeaders = options.defaultHeaders || {};
|
||||
options.defaultHeaders[Constants.HttpHeaders.CacheControl] = "no-cache";
|
||||
|
@ -68,12 +68,12 @@ export class CosmosClient {
|
|||
options.defaultHeaders[Constants.HttpHeaders.ConsistencyLevel] = options.consistencyLevel;
|
||||
}
|
||||
|
||||
const platformDefaultHeaders = Platform.getPlatformDefaultHeaders() || {};
|
||||
const platformDefaultHeaders = getPlatformDefaultHeaders() || {};
|
||||
for (const platformDefaultHeader of Object.keys(platformDefaultHeaders)) {
|
||||
options.defaultHeaders[platformDefaultHeader] = platformDefaultHeaders[platformDefaultHeader];
|
||||
}
|
||||
|
||||
options.defaultHeaders[Constants.HttpHeaders.UserAgent] = Platform.getUserAgent();
|
||||
options.defaultHeaders[Constants.HttpHeaders.UserAgent] = getUserAgent();
|
||||
|
||||
if (!this.options.agent) {
|
||||
// Initialize request agent
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Constants, Helper, ResourceType } from "./common";
|
||||
import { Constants, isReadRequest, ResourceType } from "./common";
|
||||
import { CosmosClientOptions } from "./CosmosClientOptions";
|
||||
import { DatabaseAccount, Location } from "./documents";
|
||||
import { LocationInfo } from "./LocationInfo";
|
||||
|
@ -115,7 +115,7 @@ export class LocationCache {
|
|||
// then default to the first two write locations, alternating (or the default endpoint)
|
||||
if (
|
||||
request.locationRouting.ignorePreferredLocation ||
|
||||
(!Helper.isReadRequest(request) && !this.canUseMultipleWriteLocations(request))
|
||||
(!isReadRequest(request) && !this.canUseMultipleWriteLocations(request))
|
||||
) {
|
||||
const currentInfo = this.locationInfo;
|
||||
if (currentInfo.orderedWriteLocations.length > 0) {
|
||||
|
@ -127,9 +127,7 @@ export class LocationCache {
|
|||
}
|
||||
} else {
|
||||
// If we're using preferred regions, then choose the correct endpoint based on the location index
|
||||
const endpoints = Helper.isReadRequest(request)
|
||||
? this.locationInfo.readEndpoints
|
||||
: this.locationInfo.writeEndpoints;
|
||||
const endpoints = isReadRequest(request) ? this.locationInfo.readEndpoints : this.locationInfo.writeEndpoints;
|
||||
return endpoints[locationIndex % endpoints.length];
|
||||
}
|
||||
}
|
||||
|
|
253
src/auth.ts
253
src/auth.ts
|
@ -1,6 +1,6 @@
|
|||
import createHmac from "create-hmac";
|
||||
import { PermissionDefinition } from "./client";
|
||||
import { Helper } from "./common";
|
||||
import { getResourceIdFromPath } from "./common";
|
||||
import { IHeaders } from "./queryExecutionContext";
|
||||
|
||||
/** @hidden */
|
||||
|
@ -31,140 +31,133 @@ export interface AuthOptions {
|
|||
permissionFeed?: PermissionDefinition[]; // TODO: any
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
export class AuthHandler {
|
||||
public static async getAuthorizationHeader(
|
||||
authOptions: AuthOptions,
|
||||
verb: string,
|
||||
path: string,
|
||||
resourceId: string,
|
||||
resourceType: string,
|
||||
headers: IHeaders
|
||||
): Promise<string> {
|
||||
if (authOptions.permissionFeed) {
|
||||
authOptions.resourceTokens = {};
|
||||
for (const permission of authOptions.permissionFeed) {
|
||||
const id = Helper.getResourceIdFromPath(permission.resource);
|
||||
if (!id) {
|
||||
throw new Error(`authorization error: ${id} \
|
||||
export async function getAuthorizationHeader(
|
||||
authOptions: AuthOptions,
|
||||
verb: string,
|
||||
path: string,
|
||||
resourceId: string,
|
||||
resourceType: string,
|
||||
headers: IHeaders
|
||||
): Promise<string> {
|
||||
if (authOptions.permissionFeed) {
|
||||
authOptions.resourceTokens = {};
|
||||
for (const permission of authOptions.permissionFeed) {
|
||||
const id = getResourceIdFromPath(permission.resource);
|
||||
if (!id) {
|
||||
throw new Error(`authorization error: ${id} \
|
||||
is an invalid resourceId in permissionFeed`);
|
||||
}
|
||||
|
||||
authOptions.resourceTokens[id] = (permission as any)._token; // TODO: any
|
||||
}
|
||||
}
|
||||
|
||||
if (authOptions.masterKey || authOptions.key) {
|
||||
const key = authOptions.masterKey || authOptions.key;
|
||||
return encodeURIComponent(
|
||||
AuthHandler.getAuthorizationTokenUsingMasterKey(verb, resourceId, resourceType, headers, key)
|
||||
);
|
||||
} else if (authOptions.resourceTokens) {
|
||||
return encodeURIComponent(
|
||||
AuthHandler.getAuthorizationTokenUsingResourceTokens(authOptions.resourceTokens, path, resourceId)
|
||||
);
|
||||
} else if (authOptions.tokenProvider) {
|
||||
return encodeURIComponent(
|
||||
await AuthHandler.getAuthorizationTokenUsingTokenProvider(authOptions.tokenProvider, {
|
||||
verb,
|
||||
path,
|
||||
resourceId,
|
||||
resourceType,
|
||||
headers
|
||||
})
|
||||
);
|
||||
authOptions.resourceTokens[id] = (permission as any)._token; // TODO: any
|
||||
}
|
||||
}
|
||||
|
||||
private static getAuthorizationTokenUsingMasterKey(
|
||||
verb: string,
|
||||
resourceId: string,
|
||||
resourceType: string,
|
||||
headers: IHeaders,
|
||||
masterKey: string
|
||||
) {
|
||||
const key = Buffer.from(masterKey, "base64");
|
||||
|
||||
const text =
|
||||
(verb || "").toLowerCase() +
|
||||
"\n" +
|
||||
(resourceType || "").toLowerCase() +
|
||||
"\n" +
|
||||
(resourceId || "") +
|
||||
"\n" +
|
||||
((headers["x-ms-date"] as string) || "").toLowerCase() +
|
||||
"\n" +
|
||||
((headers["date"] as string) || "").toLowerCase() +
|
||||
"\n";
|
||||
|
||||
const body = Buffer.from(text, "utf8");
|
||||
const signature = createHmac("sha256", key)
|
||||
.update(body)
|
||||
.digest("base64");
|
||||
const MasterToken = "master";
|
||||
const TokenVersion = "1.0";
|
||||
|
||||
return `type=${MasterToken}&ver=${TokenVersion}&sig=${signature}`;
|
||||
}
|
||||
|
||||
// TODO: Resource tokens
|
||||
private static getAuthorizationTokenUsingResourceTokens(
|
||||
resourceTokens: { [resourceId: string]: string },
|
||||
path: string,
|
||||
resourceId: string
|
||||
) {
|
||||
if (resourceTokens && Object.keys(resourceTokens).length > 0) {
|
||||
// For database account access(through getDatabaseAccount API), path and resourceId are "",
|
||||
// so in this case we return the first token to be used for creating the auth header as the
|
||||
// service will accept any token in this case
|
||||
if (!path && !resourceId) {
|
||||
return resourceTokens[Object.keys(resourceTokens)[0]];
|
||||
}
|
||||
|
||||
if (resourceId && resourceTokens[resourceId]) {
|
||||
return resourceTokens[resourceId];
|
||||
}
|
||||
|
||||
// minimum valid path /dbs
|
||||
if (!path || path.length < 4) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// remove '/' from left and right of path
|
||||
path = path[0] === "/" ? path.substring(1) : path;
|
||||
path = path[path.length - 1] === "/" ? path.substring(0, path.length - 1) : path;
|
||||
|
||||
const pathSegments = (path && path.split("/")) || [];
|
||||
|
||||
// if it's an incomplete path like /dbs/db1/colls/, start from the paretn resource
|
||||
let index = pathSegments.length % 2 === 0 ? pathSegments.length - 1 : pathSegments.length - 2;
|
||||
for (; index > 0; index -= 2) {
|
||||
const id = decodeURI(pathSegments[index]);
|
||||
if (resourceTokens[id]) {
|
||||
return resourceTokens[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static getAuthorizationTokenUsingTokenProvider(
|
||||
tokenProvider: ITokenProvider,
|
||||
requestInfo: IRequestInfo
|
||||
): Promise<string> {
|
||||
requestInfo.getAuthorizationTokenUsingMasterKey = AuthHandler.getAuthorizationTokenUsingMasterKey;
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const callback = (err: Error, token: string) => {
|
||||
if (reject) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(token);
|
||||
};
|
||||
|
||||
const results = tokenProvider.getToken(requestInfo, callback);
|
||||
if (results.then && typeof results.then === "function") {
|
||||
resolve(await results);
|
||||
}
|
||||
});
|
||||
if (authOptions.masterKey || authOptions.key) {
|
||||
const key = authOptions.masterKey || authOptions.key;
|
||||
return encodeURIComponent(getAuthorizationTokenUsingMasterKey(verb, resourceId, resourceType, headers, key));
|
||||
} else if (authOptions.resourceTokens) {
|
||||
return encodeURIComponent(getAuthorizationTokenUsingResourceTokens(authOptions.resourceTokens, path, resourceId));
|
||||
} else if (authOptions.tokenProvider) {
|
||||
return encodeURIComponent(
|
||||
await getAuthorizationTokenUsingTokenProvider(authOptions.tokenProvider, {
|
||||
verb,
|
||||
path,
|
||||
resourceId,
|
||||
resourceType,
|
||||
headers
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getAuthorizationTokenUsingMasterKey(
|
||||
verb: string,
|
||||
resourceId: string,
|
||||
resourceType: string,
|
||||
headers: IHeaders,
|
||||
masterKey: string
|
||||
) {
|
||||
const key = Buffer.from(masterKey, "base64");
|
||||
|
||||
const text =
|
||||
(verb || "").toLowerCase() +
|
||||
"\n" +
|
||||
(resourceType || "").toLowerCase() +
|
||||
"\n" +
|
||||
(resourceId || "") +
|
||||
"\n" +
|
||||
((headers["x-ms-date"] as string) || "").toLowerCase() +
|
||||
"\n" +
|
||||
((headers["date"] as string) || "").toLowerCase() +
|
||||
"\n";
|
||||
|
||||
const body = Buffer.from(text, "utf8");
|
||||
const signature = createHmac("sha256", key)
|
||||
.update(body)
|
||||
.digest("base64");
|
||||
const MasterToken = "master";
|
||||
const TokenVersion = "1.0";
|
||||
|
||||
return `type=${MasterToken}&ver=${TokenVersion}&sig=${signature}`;
|
||||
}
|
||||
|
||||
// TODO: Resource tokens
|
||||
function getAuthorizationTokenUsingResourceTokens(
|
||||
resourceTokens: { [resourceId: string]: string },
|
||||
path: string,
|
||||
resourceId: string
|
||||
) {
|
||||
if (resourceTokens && Object.keys(resourceTokens).length > 0) {
|
||||
// For database account access(through getDatabaseAccount API), path and resourceId are "",
|
||||
// so in this case we return the first token to be used for creating the auth header as the
|
||||
// service will accept any token in this case
|
||||
if (!path && !resourceId) {
|
||||
return resourceTokens[Object.keys(resourceTokens)[0]];
|
||||
}
|
||||
|
||||
if (resourceId && resourceTokens[resourceId]) {
|
||||
return resourceTokens[resourceId];
|
||||
}
|
||||
|
||||
// minimum valid path /dbs
|
||||
if (!path || path.length < 4) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// remove '/' from left and right of path
|
||||
path = path[0] === "/" ? path.substring(1) : path;
|
||||
path = path[path.length - 1] === "/" ? path.substring(0, path.length - 1) : path;
|
||||
|
||||
const pathSegments = (path && path.split("/")) || [];
|
||||
|
||||
// if it's an incomplete path like /dbs/db1/colls/, start from the paretn resource
|
||||
let index = pathSegments.length % 2 === 0 ? pathSegments.length - 1 : pathSegments.length - 2;
|
||||
for (; index > 0; index -= 2) {
|
||||
const id = decodeURI(pathSegments[index]);
|
||||
if (resourceTokens[id]) {
|
||||
return resourceTokens[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getAuthorizationTokenUsingTokenProvider(
|
||||
tokenProvider: ITokenProvider,
|
||||
requestInfo: IRequestInfo
|
||||
): Promise<string> {
|
||||
requestInfo.getAuthorizationTokenUsingMasterKey = getAuthorizationTokenUsingMasterKey;
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const callback = (err: Error, token: string) => {
|
||||
if (reject) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(token);
|
||||
};
|
||||
|
||||
const results = tokenProvider.getToken(requestInfo, callback);
|
||||
if (results.then && typeof results.then === "function") {
|
||||
resolve(await results);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Constants, Helper } from "../../common";
|
||||
import { Constants, getIdFromLink, getPathFromLink } from "../../common";
|
||||
import { RequestOptions } from "../../request";
|
||||
import { Container } from "../Container";
|
||||
import { ConflictDefinition } from "./ConflictDefinition";
|
||||
|
@ -33,8 +33,8 @@ export class Conflict {
|
|||
* @param options
|
||||
*/
|
||||
public async read(options?: RequestOptions): Promise<ConflictResponse> {
|
||||
const path = Helper.getPathFromLink(this.url, "conflicts");
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url, "conflicts");
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.read<ConflictDefinition>(path, "users", id, undefined, options);
|
||||
return { body: response.result, headers: response.headers, ref: this, conflict: this };
|
||||
|
@ -45,8 +45,8 @@ export class Conflict {
|
|||
* @param options
|
||||
*/
|
||||
public async delete(options?: RequestOptions): Promise<ConflictResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete<ConflictDefinition>(path, "conflicts", id, undefined, options);
|
||||
return { body: response.result, headers: response.headers, ref: this, conflict: this };
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper } from "../../common";
|
||||
import { getIdFromLink, getPathFromLink } from "../../common";
|
||||
import { SqlQuerySpec } from "../../queryExecutionContext";
|
||||
import { QueryIterator } from "../../queryIterator";
|
||||
import { FeedOptions } from "../../request";
|
||||
|
@ -30,8 +30,8 @@ export class Conflicts {
|
|||
*/
|
||||
public query<T>(query: SqlQuerySpec, options?: FeedOptions): QueryIterator<T>;
|
||||
public query<T>(query: SqlQuerySpec, options?: FeedOptions): QueryIterator<T> {
|
||||
const path = Helper.getPathFromLink(this.container.url, "conflicts");
|
||||
const id = Helper.getIdFromLink(this.container.url);
|
||||
const path = getPathFromLink(this.container.url, "conflicts");
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
return new QueryIterator(this.clientContext, query, options, innerOptions => {
|
||||
return this.clientContext.queryFeed(path, "conflicts", id, result => result.Conflicts, query, innerOptions);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper, UriFactory } from "../../common";
|
||||
import { createDocumentCollectionUri, getIdFromLink, getPathFromLink, isResourceValid, parsePath } from "../../common";
|
||||
import { PartitionKeyDefinition } from "../../documents";
|
||||
import { PartitionKey } from "../../index";
|
||||
import { QueryIterator } from "../../queryIterator";
|
||||
|
@ -61,7 +61,7 @@ export class Container {
|
|||
* Returns a reference URL to the resource. Used for linking in Permissions.
|
||||
*/
|
||||
public get url() {
|
||||
return UriFactory.createDocumentCollectionUri(this.database.id, this.id);
|
||||
return createDocumentCollectionUri(this.database.id, this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -138,8 +138,8 @@ export class Container {
|
|||
|
||||
/** Read the container's definition */
|
||||
public async read(options?: RequestOptions): Promise<ContainerResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.read<ContainerDefinition>(path, "colls", id, undefined, options);
|
||||
this.clientContext.partitionKeyDefinitionCache[this.url] = response.result.partitionKey;
|
||||
|
@ -154,12 +154,12 @@ export class Container {
|
|||
/** Replace the container's definition */
|
||||
public async replace(body: ContainerDefinition, options?: RequestOptions): Promise<ContainerResponse> {
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.replace<ContainerDefinition>(body, path, "colls", id, undefined, options);
|
||||
return {
|
||||
|
@ -172,8 +172,8 @@ export class Container {
|
|||
|
||||
/** Delete the container */
|
||||
public async delete(options?: RequestOptions): Promise<ContainerResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete<ContainerDefinition>(path, "colls", id, undefined, options);
|
||||
return {
|
||||
|
@ -220,7 +220,7 @@ export class Container {
|
|||
if (partitionKeyDefinition && partitionKeyDefinition.paths && partitionKeyDefinition.paths.length > 0) {
|
||||
const partitionKey: PartitionKey[] = [];
|
||||
partitionKeyDefinition.paths.forEach((path: string) => {
|
||||
const pathParts = Helper.parsePath(path);
|
||||
const pathParts = parsePath(path);
|
||||
|
||||
let obj = document;
|
||||
for (const part of pathParts) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper, StatusCodes } from "../../common";
|
||||
import { HeaderUtils, SqlQuerySpec } from "../../queryExecutionContext";
|
||||
import { getIdFromLink, getPathFromLink, isResourceValid, StatusCodes } from "../../common";
|
||||
import { mergeHeaders, SqlQuerySpec } from "../../queryExecutionContext";
|
||||
import { QueryIterator } from "../../queryIterator";
|
||||
import { FeedOptions, RequestOptions } from "../../request";
|
||||
import { Database } from "../Database";
|
||||
|
@ -57,8 +57,8 @@ export class Containers {
|
|||
*/
|
||||
public query<T>(query: SqlQuerySpec, options?: FeedOptions): QueryIterator<T>;
|
||||
public query<T>(query: SqlQuerySpec, options?: FeedOptions): QueryIterator<T> {
|
||||
const path = Helper.getPathFromLink(this.database.url, "colls");
|
||||
const id = Helper.getIdFromLink(this.database.url);
|
||||
const path = getPathFromLink(this.database.url, "colls");
|
||||
const id = getIdFromLink(this.database.url);
|
||||
|
||||
return new QueryIterator(this.clientContext, query, options, innerOptions => {
|
||||
return this.clientContext.queryFeed<ContainerDefinition>(
|
||||
|
@ -91,11 +91,11 @@ export class Containers {
|
|||
*/
|
||||
public async create(body: ContainerDefinition, options?: RequestOptions): Promise<ContainerResponse> {
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
const path = Helper.getPathFromLink(this.database.url, "colls");
|
||||
const id = Helper.getIdFromLink(this.database.url);
|
||||
const path = getPathFromLink(this.database.url, "colls");
|
||||
const id = getIdFromLink(this.database.url);
|
||||
|
||||
const response = await this.clientContext.create<ContainerDefinition>(body, path, "colls", id, undefined, options);
|
||||
const ref = new Container(this.database, response.result.id, this.clientContext);
|
||||
|
@ -141,7 +141,7 @@ export class Containers {
|
|||
if (err.code === StatusCodes.NotFound) {
|
||||
const createResponse = await this.create(body, options);
|
||||
// Must merge the headers to capture RU costskaty
|
||||
HeaderUtils.mergeHeaders(createResponse.headers, err.headers);
|
||||
mergeHeaders(createResponse.headers, err.headers);
|
||||
return createResponse;
|
||||
} else {
|
||||
throw err;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper, UriFactory } from "../../common";
|
||||
import { createDatabaseUri, getIdFromLink, getPathFromLink } from "../../common";
|
||||
import { CosmosClient } from "../../CosmosClient";
|
||||
import { RequestOptions } from "../../request";
|
||||
import { Container, Containers } from "../Container";
|
||||
|
@ -40,7 +40,7 @@ export class Database {
|
|||
* Returns a reference URL to the resource. Used for linking in Permissions.
|
||||
*/
|
||||
public get url() {
|
||||
return UriFactory.createDatabaseUri(this.id);
|
||||
return createDatabaseUri(this.id);
|
||||
}
|
||||
|
||||
/** Returns a new {@link Database} instance.
|
||||
|
@ -77,8 +77,8 @@ export class Database {
|
|||
|
||||
/** Read the definition of the given Database. */
|
||||
public async read(options?: RequestOptions): Promise<DatabaseResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
const response = await this.clientContext.read<DatabaseDefinition>(path, "dbs", id, undefined, options);
|
||||
return {
|
||||
body: response.result,
|
||||
|
@ -90,8 +90,8 @@ export class Database {
|
|||
|
||||
/** Delete the given Database. */
|
||||
public async delete(options?: RequestOptions): Promise<DatabaseResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
const response = await this.clientContext.delete<DatabaseDefinition>(path, "dbs", id, undefined, options);
|
||||
return {
|
||||
body: response.result,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper, StatusCodes } from "../../common";
|
||||
import { isResourceValid, StatusCodes } from "../../common";
|
||||
import { CosmosClient } from "../../CosmosClient";
|
||||
import { FetchFunctionCallback, HeaderUtils, SqlQuerySpec } from "../../queryExecutionContext";
|
||||
import { FetchFunctionCallback, mergeHeaders, SqlQuerySpec } from "../../queryExecutionContext";
|
||||
import { QueryIterator } from "../../queryIterator";
|
||||
import { FeedOptions, RequestOptions } from "../../request";
|
||||
import { Resource } from "../Resource";
|
||||
|
@ -83,7 +83,7 @@ export class Databases {
|
|||
*/
|
||||
public async create(body: DatabaseDefinition, options?: RequestOptions): Promise<DatabaseResponse> {
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ export class Databases {
|
|||
if (err.code === StatusCodes.NotFound) {
|
||||
const createResponse = await this.create(body, options);
|
||||
// Must merge the headers to capture RU costskaty
|
||||
HeaderUtils.mergeHeaders(createResponse.headers, err.headers);
|
||||
mergeHeaders(createResponse.headers, err.headers);
|
||||
return createResponse;
|
||||
} else {
|
||||
throw err;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper, UriFactory } from "../../common";
|
||||
import { createDocumentUri, getIdFromLink, getPathFromLink, isResourceValid } from "../../common";
|
||||
import { RequestOptions } from "../../request";
|
||||
import { Container } from "../Container";
|
||||
import { Resource } from "../Resource";
|
||||
import { ItemDefinition } from "./ItemDefinition";
|
||||
import { ItemResponse } from "./ItemResponse";
|
||||
|
||||
|
@ -16,7 +15,7 @@ export class Item {
|
|||
* Returns a reference URL to the resource. Used for linking in Permissions.
|
||||
*/
|
||||
public get url() {
|
||||
return UriFactory.createDocumentUri(this.container.database.id, this.container.id, this.id);
|
||||
return createDocumentUri(this.container.database.id, this.container.id, this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,8 +71,8 @@ export class Item {
|
|||
if ((!options || !options.partitionKey) && this.primaryKey) {
|
||||
options.partitionKey = this.primaryKey;
|
||||
}
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
const response = await this.clientContext.read<T>(path, "docs", id, undefined, options);
|
||||
|
||||
return {
|
||||
|
@ -116,12 +115,12 @@ export class Item {
|
|||
}
|
||||
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.replace<T>(body, path, "docs", id, undefined, options);
|
||||
return {
|
||||
|
@ -151,8 +150,8 @@ export class Item {
|
|||
if ((!options || !options.partitionKey) && this.primaryKey) {
|
||||
options.partitionKey = this.primaryKey;
|
||||
}
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete<T>(path, "docs", id, undefined, options);
|
||||
return {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { ChangeFeedIterator } from "../../ChangeFeedIterator";
|
||||
import { ChangeFeedOptions } from "../../ChangeFeedOptions";
|
||||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper } from "../../common";
|
||||
import { generateGuidId, getIdFromLink, getPathFromLink, isResourceValid } from "../../common";
|
||||
import { FetchFunctionCallback, SqlQuerySpec } from "../../queryExecutionContext";
|
||||
import { QueryIterator } from "../../queryIterator";
|
||||
import { FeedOptions, RequestOptions } from "../../request";
|
||||
|
@ -62,8 +62,8 @@ export class Items {
|
|||
*/
|
||||
public query<T>(query: string | SqlQuerySpec, options?: FeedOptions): QueryIterator<T>;
|
||||
public query<T>(query: string | SqlQuerySpec, options?: FeedOptions): QueryIterator<T> {
|
||||
const path = Helper.getPathFromLink(this.container.url, "docs");
|
||||
const id = Helper.getIdFromLink(this.container.url);
|
||||
const path = getPathFromLink(this.container.url, "docs");
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
const fetchFunction: FetchFunctionCallback = (innerOptions: FeedOptions) => {
|
||||
return this.clientContext.queryFeed(
|
||||
|
@ -135,8 +135,8 @@ export class Items {
|
|||
throw new Error("changeFeedOptions must be a valid object");
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.container.url, "docs");
|
||||
const id = Helper.getIdFromLink(this.container.url);
|
||||
const path = getPathFromLink(this.container.url, "docs");
|
||||
const id = getIdFromLink(this.container.url);
|
||||
return new ChangeFeedIterator<T>(
|
||||
this.clientContext,
|
||||
id,
|
||||
|
@ -211,16 +211,16 @@ export class Items {
|
|||
// Generate random document id if the id is missing in the payload and
|
||||
// options.disableAutomaticIdGeneration != true
|
||||
if ((body.id === undefined || body.id === "") && !options.disableAutomaticIdGeneration) {
|
||||
body.id = Helper.generateGuidId();
|
||||
body.id = generateGuidId();
|
||||
}
|
||||
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.container.url, "docs");
|
||||
const id = Helper.getIdFromLink(this.container.url);
|
||||
const path = getPathFromLink(this.container.url, "docs");
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
const response = await this.clientContext.create<T>(body, path, "docs", id, undefined, options);
|
||||
|
||||
|
@ -268,16 +268,16 @@ export class Items {
|
|||
// Generate random document id if the id is missing in the payload and
|
||||
// options.disableAutomaticIdGeneration != true
|
||||
if ((body.id === undefined || body.id === "") && !options.disableAutomaticIdGeneration) {
|
||||
body.id = Helper.generateGuidId();
|
||||
body.id = generateGuidId();
|
||||
}
|
||||
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.container.url, "docs");
|
||||
const id = Helper.getIdFromLink(this.container.url);
|
||||
const path = getPathFromLink(this.container.url, "docs");
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
const response = (await this.clientContext.upsert<T>(body, path, "docs", id, undefined, options)) as T & Resource;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Constants, Helper } from "../../common";
|
||||
import { Constants, isResourceValid } from "../../common";
|
||||
import { CosmosClient } from "../../CosmosClient";
|
||||
import { RequestOptions } from "../../request";
|
||||
import { OfferDefinition } from "./OfferDefinition";
|
||||
|
@ -44,7 +44,7 @@ export class Offer {
|
|||
*/
|
||||
public async replace(body: OfferDefinition, options?: RequestOptions): Promise<OfferResponse> {
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
const response = await this.clientContext.replace<OfferDefinition>(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper, UriFactory } from "../../common";
|
||||
import { createPermissionUri, getIdFromLink, getPathFromLink, isResourceValid } from "../../common";
|
||||
import { RequestOptions } from "../../request/RequestOptions";
|
||||
import { User } from "../User";
|
||||
import { PermissionBody } from "./PermissionBody";
|
||||
|
@ -16,7 +16,7 @@ export class Permission {
|
|||
* Returns a reference URL to the resource. Used for linking in Permissions.
|
||||
*/
|
||||
public get url() {
|
||||
return UriFactory.createPermissionUri(this.user.database.id, this.user.id, this.id);
|
||||
return createPermissionUri(this.user.database.id, this.user.id, this.id);
|
||||
}
|
||||
/**
|
||||
* @hidden
|
||||
|
@ -30,8 +30,8 @@ export class Permission {
|
|||
* @param options
|
||||
*/
|
||||
public async read(options?: RequestOptions): Promise<PermissionResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.read<PermissionDefinition & PermissionBody>(
|
||||
path,
|
||||
|
@ -55,12 +55,12 @@ export class Permission {
|
|||
*/
|
||||
public async replace(body: PermissionDefinition, options?: RequestOptions): Promise<PermissionResponse> {
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.replace<PermissionDefinition & PermissionBody>(
|
||||
body,
|
||||
|
@ -83,8 +83,8 @@ export class Permission {
|
|||
* @param options
|
||||
*/
|
||||
public async delete(options?: RequestOptions): Promise<PermissionResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete<PermissionDefinition & PermissionBody>(
|
||||
path,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper } from "../../common";
|
||||
import { getIdFromLink, getPathFromLink, isResourceValid } from "../../common";
|
||||
import { SqlQuerySpec } from "../../queryExecutionContext";
|
||||
import { QueryIterator } from "../../queryIterator";
|
||||
import { FeedOptions, RequestOptions } from "../../request";
|
||||
|
@ -35,8 +35,8 @@ export class Permissions {
|
|||
*/
|
||||
public query<T>(query: SqlQuerySpec, options?: FeedOptions): QueryIterator<T>;
|
||||
public query<T>(query: SqlQuerySpec, options?: FeedOptions): QueryIterator<T> {
|
||||
const path = Helper.getPathFromLink(this.user.url, "permissions");
|
||||
const id = Helper.getIdFromLink(this.user.url);
|
||||
const path = getPathFromLink(this.user.url, "permissions");
|
||||
const id = getIdFromLink(this.user.url);
|
||||
|
||||
return new QueryIterator(this.clientContext, query, options, innerOptions => {
|
||||
return this.clientContext.queryFeed(path, "permissions", id, result => result.Permissions, query, innerOptions);
|
||||
|
@ -64,12 +64,12 @@ export class Permissions {
|
|||
*/
|
||||
public async create(body: PermissionDefinition, options?: RequestOptions): Promise<PermissionResponse> {
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.user.url, "permissions");
|
||||
const id = Helper.getIdFromLink(this.user.url);
|
||||
const path = getPathFromLink(this.user.url, "permissions");
|
||||
const id = getIdFromLink(this.user.url);
|
||||
|
||||
const response = await this.clientContext.create<PermissionDefinition, PermissionBody>(
|
||||
body,
|
||||
|
@ -96,12 +96,12 @@ export class Permissions {
|
|||
*/
|
||||
public async upsert(body: PermissionDefinition, options?: RequestOptions): Promise<PermissionResponse> {
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.user.url, "permissions");
|
||||
const id = Helper.getIdFromLink(this.user.url);
|
||||
const path = getPathFromLink(this.user.url, "permissions");
|
||||
const id = getIdFromLink(this.user.url);
|
||||
|
||||
const response = await this.clientContext.upsert<PermissionDefinition, PermissionBody>(
|
||||
body,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper, UriFactory } from "../../common";
|
||||
import { createStoredProcedureUri, getIdFromLink, getPathFromLink, isResourceValid } from "../../common";
|
||||
import { CosmosResponse, RequestOptions } from "../../request";
|
||||
import { Container } from "../Container";
|
||||
import { StoredProcedureDefinition } from "./StoredProcedureDefinition";
|
||||
|
@ -15,7 +15,7 @@ export class StoredProcedure {
|
|||
* Returns a reference URL to the resource. Used for linking in Permissions.
|
||||
*/
|
||||
public get url() {
|
||||
return UriFactory.createStoredProcedureUri(this.container.database.id, this.container.id, this.id);
|
||||
return createStoredProcedureUri(this.container.database.id, this.container.id, this.id);
|
||||
}
|
||||
/**
|
||||
* Creates a new instance of {@link StoredProcedure} linked to the parent {@link Container}.
|
||||
|
@ -34,8 +34,8 @@ export class StoredProcedure {
|
|||
* @param options
|
||||
*/
|
||||
public async read(options?: RequestOptions): Promise<StoredProcedureResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
const response = await this.clientContext.read<StoredProcedureDefinition>(path, "sprocs", id, undefined, options);
|
||||
|
||||
return { body: response.result, headers: response.headers, ref: this, storedProcedure: this, sproc: this };
|
||||
|
@ -52,12 +52,12 @@ export class StoredProcedure {
|
|||
}
|
||||
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.replace<StoredProcedureDefinition>(
|
||||
body,
|
||||
|
@ -76,8 +76,8 @@ export class StoredProcedure {
|
|||
* @param options
|
||||
*/
|
||||
public async delete(options?: RequestOptions): Promise<StoredProcedureResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete<StoredProcedureDefinition>(path, "sprocs", id, undefined, options);
|
||||
return { body: response.result, headers: response.headers, ref: this, storedProcedure: this, sproc: this };
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper } from "../../common";
|
||||
import { getIdFromLink, getPathFromLink, isResourceValid } from "../../common";
|
||||
import { SqlQuerySpec } from "../../queryExecutionContext";
|
||||
import { QueryIterator } from "../../queryIterator";
|
||||
import { FeedOptions, RequestOptions } from "../../request";
|
||||
|
@ -54,8 +54,8 @@ export class StoredProcedures {
|
|||
*/
|
||||
public query<T>(query: SqlQuerySpec, options?: FeedOptions): QueryIterator<T>;
|
||||
public query<T>(query: SqlQuerySpec, options?: FeedOptions): QueryIterator<T> {
|
||||
const path = Helper.getPathFromLink(this.container.url, "sprocs");
|
||||
const id = Helper.getIdFromLink(this.container.url);
|
||||
const path = getPathFromLink(this.container.url, "sprocs");
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
return new QueryIterator(this.clientContext, query, options, innerOptions => {
|
||||
return this.clientContext.queryFeed(path, "sprocs", id, result => result.StoredProcedures, query, innerOptions);
|
||||
|
@ -89,12 +89,12 @@ export class StoredProcedures {
|
|||
}
|
||||
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.container.url, "sprocs");
|
||||
const id = Helper.getIdFromLink(this.container.url);
|
||||
const path = getPathFromLink(this.container.url, "sprocs");
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
const response = await this.clientContext.create<StoredProcedureDefinition>(
|
||||
body,
|
||||
|
@ -124,12 +124,12 @@ export class StoredProcedures {
|
|||
}
|
||||
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.container.url, "sprocs");
|
||||
const id = Helper.getIdFromLink(this.container.url);
|
||||
const path = getPathFromLink(this.container.url, "sprocs");
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
const response = await this.clientContext.upsert<StoredProcedureDefinition>(
|
||||
body,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper, UriFactory } from "../../common";
|
||||
import { createTriggerUri, getIdFromLink, getPathFromLink, isResourceValid } from "../../common";
|
||||
import { CosmosClient } from "../../CosmosClient";
|
||||
import { RequestOptions } from "../../request";
|
||||
import { Container } from "../Container";
|
||||
|
@ -16,7 +16,7 @@ export class Trigger {
|
|||
* Returns a reference URL to the resource. Used for linking in Permissions.
|
||||
*/
|
||||
public get url() {
|
||||
return UriFactory.createTriggerUri(this.container.database.id, this.container.id, this.id);
|
||||
return createTriggerUri(this.container.database.id, this.container.id, this.id);
|
||||
}
|
||||
|
||||
private client: CosmosClient;
|
||||
|
@ -39,8 +39,8 @@ export class Trigger {
|
|||
* @param options
|
||||
*/
|
||||
public async read(options?: RequestOptions): Promise<TriggerResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.read<TriggerDefinition>(path, "triggers", id, undefined, options);
|
||||
return { body: response.result, headers: response.headers, ref: this, trigger: this };
|
||||
|
@ -57,12 +57,12 @@ export class Trigger {
|
|||
}
|
||||
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.replace<TriggerDefinition>(
|
||||
body,
|
||||
|
@ -81,8 +81,8 @@ export class Trigger {
|
|||
* @param options
|
||||
*/
|
||||
public async delete(options?: RequestOptions): Promise<TriggerResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete<TriggerDefinition>(path, "triggers", id, undefined, options);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper } from "../../common";
|
||||
import { getIdFromLink, getPathFromLink, isResourceValid } from "../../common";
|
||||
import { SqlQuerySpec } from "../../queryExecutionContext";
|
||||
import { QueryIterator } from "../../queryIterator";
|
||||
import { FeedOptions, RequestOptions } from "../../request";
|
||||
|
@ -34,8 +34,8 @@ export class Triggers {
|
|||
*/
|
||||
public query<T>(query: SqlQuerySpec, options?: FeedOptions): QueryIterator<T>;
|
||||
public query<T>(query: SqlQuerySpec, options?: FeedOptions): QueryIterator<T> {
|
||||
const path = Helper.getPathFromLink(this.container.url, "triggers");
|
||||
const id = Helper.getIdFromLink(this.container.url);
|
||||
const path = getPathFromLink(this.container.url, "triggers");
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
return new QueryIterator(this.clientContext, query, options, innerOptions => {
|
||||
return this.clientContext.queryFeed(path, "triggers", id, result => result.Triggers, query, innerOptions);
|
||||
|
@ -69,12 +69,12 @@ export class Triggers {
|
|||
}
|
||||
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.container.url, "triggers");
|
||||
const id = Helper.getIdFromLink(this.container.url);
|
||||
const path = getPathFromLink(this.container.url, "triggers");
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
const response = await this.clientContext.create<TriggerDefinition>(body, path, "triggers", id, undefined, options);
|
||||
const ref = new Trigger(this.container, response.result.id, this.clientContext);
|
||||
|
@ -97,12 +97,12 @@ export class Triggers {
|
|||
}
|
||||
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.container.url, "triggers");
|
||||
const id = Helper.getIdFromLink(this.container.url);
|
||||
const path = getPathFromLink(this.container.url, "triggers");
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
const response = await this.clientContext.upsert<TriggerDefinition>(body, path, "triggers", id, undefined, options);
|
||||
const ref = new Trigger(this.container, response.result.id, this.clientContext);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper, UriFactory } from "../../common";
|
||||
import { createUserUri, getIdFromLink, getPathFromLink, isResourceValid } from "../../common";
|
||||
import { RequestOptions } from "../../request";
|
||||
import { Database } from "../Database";
|
||||
import { Permission, Permissions } from "../Permission";
|
||||
|
@ -24,7 +24,7 @@ export class User {
|
|||
* Returns a reference URL to the resource. Used for linking in Permissions.
|
||||
*/
|
||||
public get url() {
|
||||
return UriFactory.createUserUri(this.database.id, this.id);
|
||||
return createUserUri(this.database.id, this.id);
|
||||
}
|
||||
/**
|
||||
* @hidden
|
||||
|
@ -54,8 +54,8 @@ export class User {
|
|||
* @param options
|
||||
*/
|
||||
public async read(options?: RequestOptions): Promise<UserResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
const response = await this.clientContext.read<UserDefinition>(path, "users", id, undefined, options);
|
||||
return { body: response.result, headers: response.headers, ref: this, user: this };
|
||||
}
|
||||
|
@ -67,12 +67,12 @@ export class User {
|
|||
*/
|
||||
public async replace(body: UserDefinition, options?: RequestOptions): Promise<UserResponse> {
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.replace<UserDefinition>(body, path, "users", id, undefined, options);
|
||||
return { body: response.result, headers: response.headers, ref: this, user: this };
|
||||
|
@ -83,8 +83,8 @@ export class User {
|
|||
* @param options
|
||||
*/
|
||||
public async delete(options?: RequestOptions): Promise<UserResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete<UserDefinition>(path, "users", id, undefined, options);
|
||||
return { body: response.result, headers: response.headers, ref: this, user: this };
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper } from "../../common";
|
||||
import { getIdFromLink, getPathFromLink, isResourceValid } from "../../common";
|
||||
import { SqlQuerySpec } from "../../queryExecutionContext";
|
||||
import { QueryIterator } from "../../queryIterator";
|
||||
import { FeedOptions, RequestOptions } from "../../request";
|
||||
|
@ -34,8 +34,8 @@ export class Users {
|
|||
*/
|
||||
public query<T>(query: SqlQuerySpec, options?: FeedOptions): QueryIterator<T>;
|
||||
public query<T>(query: SqlQuerySpec, options?: FeedOptions): QueryIterator<T> {
|
||||
const path = Helper.getPathFromLink(this.database.url, "users");
|
||||
const id = Helper.getIdFromLink(this.database.url);
|
||||
const path = getPathFromLink(this.database.url, "users");
|
||||
const id = getIdFromLink(this.database.url);
|
||||
|
||||
return new QueryIterator(this.clientContext, query, options, innerOptions => {
|
||||
return this.clientContext.queryFeed(path, "users", id, result => result.Users, query, innerOptions);
|
||||
|
@ -61,12 +61,12 @@ export class Users {
|
|||
*/
|
||||
public async create(body: UserDefinition, options?: RequestOptions): Promise<UserResponse> {
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.database.url, "users");
|
||||
const id = Helper.getIdFromLink(this.database.url);
|
||||
const path = getPathFromLink(this.database.url, "users");
|
||||
const id = getIdFromLink(this.database.url);
|
||||
const response = await this.clientContext.create<UserDefinition>(body, path, "users", id, undefined, options);
|
||||
const ref = new User(this.database, response.result.id, this.clientContext);
|
||||
return { body: response.result, headers: response.headers, ref, user: ref };
|
||||
|
@ -79,12 +79,12 @@ export class Users {
|
|||
*/
|
||||
public async upsert(body: UserDefinition, options?: RequestOptions): Promise<UserResponse> {
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.database.url, "users");
|
||||
const id = Helper.getIdFromLink(this.database.url);
|
||||
const path = getPathFromLink(this.database.url, "users");
|
||||
const id = getIdFromLink(this.database.url);
|
||||
|
||||
const response = await this.clientContext.upsert<UserDefinition>(body, path, "users", id, undefined, options);
|
||||
const ref = new User(this.database, response.result.id, this.clientContext);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper, UriFactory } from "../../common";
|
||||
import { createUserDefinedFunctionUri, getIdFromLink, getPathFromLink, isResourceValid } from "../../common";
|
||||
import { RequestOptions } from "../../request";
|
||||
import { Container } from "../Container";
|
||||
import { UserDefinedFunctionDefinition } from "./UserDefinedFunctionDefinition";
|
||||
|
@ -15,7 +15,7 @@ export class UserDefinedFunction {
|
|||
* Returns a reference URL to the resource. Used for linking in Permissions.
|
||||
*/
|
||||
public get url() {
|
||||
return UriFactory.createUserDefinedFunctionUri(this.container.database.id, this.container.id, this.id);
|
||||
return createUserDefinedFunctionUri(this.container.database.id, this.container.id, this.id);
|
||||
}
|
||||
/**
|
||||
* @hidden
|
||||
|
@ -33,8 +33,8 @@ export class UserDefinedFunction {
|
|||
* @param options
|
||||
*/
|
||||
public async read(options?: RequestOptions): Promise<UserDefinedFunctionResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.read<UserDefinedFunctionDefinition>(path, "udfs", id, undefined, options);
|
||||
return { body: response.result, headers: response.headers, ref: this, userDefinedFunction: this, udf: this };
|
||||
|
@ -54,12 +54,12 @@ export class UserDefinedFunction {
|
|||
}
|
||||
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.replace<UserDefinedFunctionDefinition>(
|
||||
body,
|
||||
|
@ -77,8 +77,8 @@ export class UserDefinedFunction {
|
|||
* @param options
|
||||
*/
|
||||
public async delete(options?: RequestOptions): Promise<UserDefinedFunctionResponse> {
|
||||
const path = Helper.getPathFromLink(this.url);
|
||||
const id = Helper.getIdFromLink(this.url);
|
||||
const path = getPathFromLink(this.url);
|
||||
const id = getIdFromLink(this.url);
|
||||
|
||||
const response = await this.clientContext.delete(path, "udfs", id, undefined, options);
|
||||
return { body: response.result, headers: response.headers, ref: this, userDefinedFunction: this, udf: this };
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper } from "../../common";
|
||||
import { getIdFromLink, getPathFromLink, isResourceValid } from "../../common";
|
||||
import { SqlQuerySpec } from "../../queryExecutionContext";
|
||||
import { QueryIterator } from "../../queryIterator";
|
||||
import { FeedOptions, RequestOptions } from "../../request";
|
||||
|
@ -34,8 +34,8 @@ export class UserDefinedFunctions {
|
|||
*/
|
||||
public query<T>(query: SqlQuerySpec, options?: FeedOptions): QueryIterator<T>;
|
||||
public query<T>(query: SqlQuerySpec, options?: FeedOptions): QueryIterator<T> {
|
||||
const path = Helper.getPathFromLink(this.container.url, "udfs");
|
||||
const id = Helper.getIdFromLink(this.container.url);
|
||||
const path = getPathFromLink(this.container.url, "udfs");
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
return new QueryIterator(this.clientContext, query, options, innerOptions => {
|
||||
return this.clientContext.queryFeed(path, "udfs", id, result => result.UserDefinedFunctions, query, innerOptions);
|
||||
|
@ -71,12 +71,12 @@ export class UserDefinedFunctions {
|
|||
}
|
||||
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.container.url, "udfs");
|
||||
const id = Helper.getIdFromLink(this.container.url);
|
||||
const path = getPathFromLink(this.container.url, "udfs");
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
const response = await this.clientContext.create<UserDefinedFunctionDefinition>(
|
||||
body,
|
||||
|
@ -107,12 +107,12 @@ export class UserDefinedFunctions {
|
|||
}
|
||||
|
||||
const err = {};
|
||||
if (!Helper.isResourceValid(body, err)) {
|
||||
if (!isResourceValid(body, err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const path = Helper.getPathFromLink(this.container.url, "udfs");
|
||||
const id = Helper.getIdFromLink(this.container.url);
|
||||
const path = getPathFromLink(this.container.url, "udfs");
|
||||
const id = getIdFromLink(this.container.url);
|
||||
|
||||
const response = await this.clientContext.upsert<UserDefinedFunctionDefinition>(
|
||||
body,
|
||||
|
|
|
@ -7,33 +7,32 @@ import { Constants } from "./index";
|
|||
const Regexes = Constants.RegularExpressions;
|
||||
|
||||
/** @hidden */
|
||||
export class Helper {
|
||||
public static jsonStringifyAndEscapeNonASCII(arg: any) {
|
||||
// TODO: better way for this? Not sure.
|
||||
// escapes non-ASCII characters as \uXXXX
|
||||
return JSON.stringify(arg).replace(/[\u0080-\uFFFF]/g, m => {
|
||||
return "\\u" + ("0000" + m.charCodeAt(0).toString(16)).slice(-4);
|
||||
});
|
||||
export function jsonStringifyAndEscapeNonASCII(arg: any) {
|
||||
// TODO: better way for this? Not sure.
|
||||
// escapes non-ASCII characters as \uXXXX
|
||||
return JSON.stringify(arg).replace(/[\u0080-\uFFFF]/g, m => {
|
||||
return "\\u" + ("0000" + m.charCodeAt(0).toString(16)).slice(-4);
|
||||
});
|
||||
}
|
||||
|
||||
export function parseLink(resourcePath: string) {
|
||||
if (resourcePath.length === 0) {
|
||||
/* for DatabaseAccount case, both type and objectBody will be undefined. */
|
||||
return {
|
||||
type: undefined,
|
||||
objectBody: undefined
|
||||
};
|
||||
}
|
||||
|
||||
public static parseLink(resourcePath: string) {
|
||||
if (resourcePath.length === 0) {
|
||||
/* for DatabaseAccount case, both type and objectBody will be undefined. */
|
||||
return {
|
||||
type: undefined,
|
||||
objectBody: undefined
|
||||
};
|
||||
}
|
||||
if (resourcePath[resourcePath.length - 1] !== "/") {
|
||||
resourcePath = resourcePath + "/";
|
||||
}
|
||||
|
||||
if (resourcePath[resourcePath.length - 1] !== "/") {
|
||||
resourcePath = resourcePath + "/";
|
||||
}
|
||||
if (resourcePath[0] !== "/") {
|
||||
resourcePath = "/" + resourcePath;
|
||||
}
|
||||
|
||||
if (resourcePath[0] !== "/") {
|
||||
resourcePath = "/" + resourcePath;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
The path will be in the form of /[resourceType]/[resourceId]/ ....
|
||||
/[resourceType]//[resourceType]/[resourceId]/ .... /[resourceType]/[resourceId]/
|
||||
or /[resourceType]/[resourceId]/ .... /[resourceType]/[resourceId]/[resourceType]/[resourceId]/ ....
|
||||
|
@ -44,285 +43,283 @@ export class Helper {
|
|||
and the type will be before it ( at length -3 )
|
||||
In the second case, to extract the resource type it will the element before last ( at length -2 )
|
||||
*/
|
||||
const pathParts = resourcePath.split("/");
|
||||
let id;
|
||||
let type;
|
||||
if (pathParts.length % 2 === 0) {
|
||||
// request in form /[resourceType]/[resourceId]/ .... /[resourceType]/[resourceId].
|
||||
id = pathParts[pathParts.length - 2];
|
||||
type = pathParts[pathParts.length - 3];
|
||||
} else {
|
||||
// request in form /[resourceType]/[resourceId]/ .... /[resourceType]/.
|
||||
id = pathParts[pathParts.length - 3];
|
||||
type = pathParts[pathParts.length - 2];
|
||||
}
|
||||
|
||||
const result = {
|
||||
type,
|
||||
objectBody: {
|
||||
id,
|
||||
self: resourcePath
|
||||
}
|
||||
};
|
||||
|
||||
return result;
|
||||
const pathParts = resourcePath.split("/");
|
||||
let id;
|
||||
let type;
|
||||
if (pathParts.length % 2 === 0) {
|
||||
// request in form /[resourceType]/[resourceId]/ .... /[resourceType]/[resourceId].
|
||||
id = pathParts[pathParts.length - 2];
|
||||
type = pathParts[pathParts.length - 3];
|
||||
} else {
|
||||
// request in form /[resourceType]/[resourceId]/ .... /[resourceType]/.
|
||||
id = pathParts[pathParts.length - 3];
|
||||
type = pathParts[pathParts.length - 2];
|
||||
}
|
||||
|
||||
public static isReadRequest(request: RequestContext): boolean {
|
||||
return (
|
||||
request.operationType === Constants.OperationTypes.Read ||
|
||||
request.operationType === Constants.OperationTypes.Query
|
||||
);
|
||||
const result = {
|
||||
type,
|
||||
objectBody: {
|
||||
id,
|
||||
self: resourcePath
|
||||
}
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function isReadRequest(request: RequestContext): boolean {
|
||||
return (
|
||||
request.operationType === Constants.OperationTypes.Read || request.operationType === Constants.OperationTypes.Query
|
||||
);
|
||||
}
|
||||
|
||||
export function sleep(time: number): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, time);
|
||||
});
|
||||
}
|
||||
|
||||
export function getContainerLink(link: string) {
|
||||
return link
|
||||
.split("/")
|
||||
.slice(0, 4)
|
||||
.join("/");
|
||||
}
|
||||
|
||||
export function trimSlashes(source: string) {
|
||||
return source
|
||||
.replace(Constants.RegularExpressions.TrimLeftSlashes, "")
|
||||
.replace(Constants.RegularExpressions.TrimRightSlashes, "");
|
||||
}
|
||||
|
||||
export function getHexaDigit() {
|
||||
return Math.floor(Math.random() * 16).toString(16);
|
||||
}
|
||||
|
||||
export function setIsUpsertHeader(headers: IHeaders) {
|
||||
if (headers === undefined || headers === null) {
|
||||
throw new Error('The "headers" parameter must not be null or undefined');
|
||||
}
|
||||
|
||||
public static sleep(time: number): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, time);
|
||||
});
|
||||
if (!(headers instanceof Object)) {
|
||||
throw new Error(`The "headers" parameter must be an instance of "Object". Actual type is: "${typeof headers}".`);
|
||||
}
|
||||
|
||||
public static getContainerLink(link: string) {
|
||||
return link
|
||||
.split("/")
|
||||
.slice(0, 4)
|
||||
.join("/");
|
||||
(headers as IHeaders)[Constants.HttpHeaders.IsUpsert] = true;
|
||||
}
|
||||
|
||||
// TODO: replace with well known library?
|
||||
export function generateGuidId() {
|
||||
let id = "";
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
id += getHexaDigit();
|
||||
}
|
||||
|
||||
public static trimSlashes(source: string) {
|
||||
return source
|
||||
.replace(Constants.RegularExpressions.TrimLeftSlashes, "")
|
||||
.replace(Constants.RegularExpressions.TrimRightSlashes, "");
|
||||
id += "-";
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
id += getHexaDigit();
|
||||
}
|
||||
|
||||
public static getHexaDigit() {
|
||||
return Math.floor(Math.random() * 16).toString(16);
|
||||
id += "-";
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
id += getHexaDigit();
|
||||
}
|
||||
|
||||
public static setIsUpsertHeader(headers: IHeaders) {
|
||||
if (headers === undefined || headers === null) {
|
||||
throw new Error('The "headers" parameter must not be null or undefined');
|
||||
}
|
||||
id += "-";
|
||||
|
||||
if (!(headers instanceof Object)) {
|
||||
throw new Error(`The "headers" parameter must be an instance of "Object". Actual type is: "${typeof headers}".`);
|
||||
}
|
||||
|
||||
(headers as IHeaders)[Constants.HttpHeaders.IsUpsert] = true;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
id += getHexaDigit();
|
||||
}
|
||||
|
||||
// TODO: replace with well known library?
|
||||
public static generateGuidId() {
|
||||
let id = "";
|
||||
id += "-";
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
id += Helper.getHexaDigit();
|
||||
}
|
||||
|
||||
id += "-";
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
id += Helper.getHexaDigit();
|
||||
}
|
||||
|
||||
id += "-";
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
id += Helper.getHexaDigit();
|
||||
}
|
||||
|
||||
id += "-";
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
id += Helper.getHexaDigit();
|
||||
}
|
||||
|
||||
id += "-";
|
||||
|
||||
for (let i = 0; i < 12; i++) {
|
||||
id += Helper.getHexaDigit();
|
||||
}
|
||||
|
||||
return id;
|
||||
for (let i = 0; i < 12; i++) {
|
||||
id += getHexaDigit();
|
||||
}
|
||||
|
||||
public static parsePath(path: string) {
|
||||
const pathParts = [];
|
||||
let currentIndex = 0;
|
||||
return id;
|
||||
}
|
||||
|
||||
const throwError = () => {
|
||||
throw new Error("Path " + path + " is invalid at index " + currentIndex);
|
||||
};
|
||||
export function parsePath(path: string) {
|
||||
const pathParts = [];
|
||||
let currentIndex = 0;
|
||||
|
||||
const getEscapedToken = () => {
|
||||
const quote = path[currentIndex];
|
||||
let newIndex = ++currentIndex;
|
||||
const throwError = () => {
|
||||
throw new Error("Path " + path + " is invalid at index " + currentIndex);
|
||||
};
|
||||
|
||||
while (true) {
|
||||
newIndex = path.indexOf(quote, newIndex);
|
||||
if (newIndex === -1) {
|
||||
throwError();
|
||||
}
|
||||
const getEscapedToken = () => {
|
||||
const quote = path[currentIndex];
|
||||
let newIndex = ++currentIndex;
|
||||
|
||||
if (path[newIndex - 1] !== "\\") {
|
||||
break;
|
||||
}
|
||||
|
||||
++newIndex;
|
||||
}
|
||||
|
||||
const token = path.substr(currentIndex, newIndex - currentIndex);
|
||||
currentIndex = newIndex + 1;
|
||||
return token;
|
||||
};
|
||||
|
||||
const getToken = () => {
|
||||
const newIndex = path.indexOf("/", currentIndex);
|
||||
let token = null;
|
||||
while (true) {
|
||||
newIndex = path.indexOf(quote, newIndex);
|
||||
if (newIndex === -1) {
|
||||
token = path.substr(currentIndex);
|
||||
currentIndex = path.length;
|
||||
} else {
|
||||
token = path.substr(currentIndex, newIndex - currentIndex);
|
||||
currentIndex = newIndex;
|
||||
}
|
||||
|
||||
token = token.trim();
|
||||
return token;
|
||||
};
|
||||
|
||||
while (currentIndex < path.length) {
|
||||
if (path[currentIndex] !== "/") {
|
||||
throwError();
|
||||
}
|
||||
|
||||
if (++currentIndex === path.length) {
|
||||
if (path[newIndex - 1] !== "\\") {
|
||||
break;
|
||||
}
|
||||
|
||||
if (path[currentIndex] === '"' || path[currentIndex] === "'") {
|
||||
pathParts.push(getEscapedToken());
|
||||
} else {
|
||||
pathParts.push(getToken());
|
||||
}
|
||||
++newIndex;
|
||||
}
|
||||
|
||||
return pathParts;
|
||||
}
|
||||
public static isResourceValid(resource: any, err: any) {
|
||||
// TODO: any TODO: code smell
|
||||
if (resource.id) {
|
||||
if (typeof resource.id !== "string") {
|
||||
err.message = "Id must be a string.";
|
||||
return false;
|
||||
}
|
||||
const token = path.substr(currentIndex, newIndex - currentIndex);
|
||||
currentIndex = newIndex + 1;
|
||||
return token;
|
||||
};
|
||||
|
||||
if (
|
||||
resource.id.indexOf("/") !== -1 ||
|
||||
resource.id.indexOf("\\") !== -1 ||
|
||||
resource.id.indexOf("?") !== -1 ||
|
||||
resource.id.indexOf("#") !== -1
|
||||
) {
|
||||
err.message = "Id contains illegal chars.";
|
||||
return false;
|
||||
}
|
||||
if (resource.id[resource.id.length - 1] === " ") {
|
||||
err.message = "Id ends with a space.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
public static getIdFromLink(resourceLink: string, isNameBased: boolean = true) {
|
||||
if (isNameBased) {
|
||||
resourceLink = Helper.trimSlashes(resourceLink);
|
||||
return resourceLink;
|
||||
const getToken = () => {
|
||||
const newIndex = path.indexOf("/", currentIndex);
|
||||
let token = null;
|
||||
if (newIndex === -1) {
|
||||
token = path.substr(currentIndex);
|
||||
currentIndex = path.length;
|
||||
} else {
|
||||
return Helper.parseLink(resourceLink).objectBody.id.toLowerCase();
|
||||
token = path.substr(currentIndex, newIndex - currentIndex);
|
||||
currentIndex = newIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
public static getPathFromLink(resourceLink: string, resourceType?: string, isNameBased: boolean = true) {
|
||||
if (isNameBased) {
|
||||
resourceLink = Helper.trimSlashes(resourceLink);
|
||||
if (resourceType) {
|
||||
return "/" + encodeURI(resourceLink) + "/" + resourceType;
|
||||
} else {
|
||||
return "/" + encodeURI(resourceLink);
|
||||
}
|
||||
token = token.trim();
|
||||
return token;
|
||||
};
|
||||
|
||||
while (currentIndex < path.length) {
|
||||
if (path[currentIndex] !== "/") {
|
||||
throwError();
|
||||
}
|
||||
|
||||
if (++currentIndex === path.length) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (path[currentIndex] === '"' || path[currentIndex] === "'") {
|
||||
pathParts.push(getEscapedToken());
|
||||
} else {
|
||||
if (resourceType) {
|
||||
return "/" + resourceLink + resourceType + "/";
|
||||
} else {
|
||||
return "/" + resourceLink;
|
||||
}
|
||||
pathParts.push(getToken());
|
||||
}
|
||||
}
|
||||
public static isStringNullOrEmpty(inputString: string) {
|
||||
// checks whether string is null, undefined, empty or only contains space
|
||||
return !inputString || /^\s*$/.test(inputString);
|
||||
|
||||
return pathParts;
|
||||
}
|
||||
export function isResourceValid(resource: any, err: any) {
|
||||
// TODO: any TODO: code smell
|
||||
if (resource.id) {
|
||||
if (typeof resource.id !== "string") {
|
||||
err.message = "Id must be a string.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
resource.id.indexOf("/") !== -1 ||
|
||||
resource.id.indexOf("\\") !== -1 ||
|
||||
resource.id.indexOf("?") !== -1 ||
|
||||
resource.id.indexOf("#") !== -1
|
||||
) {
|
||||
err.message = "Id contains illegal chars.";
|
||||
return false;
|
||||
}
|
||||
if (resource.id[resource.id.length - 1] === " ") {
|
||||
err.message = "Id ends with a space.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static trimSlashFromLeftAndRight(inputString: string) {
|
||||
if (typeof inputString !== "string") {
|
||||
throw new Error("invalid input: input is not string");
|
||||
}
|
||||
|
||||
return inputString.replace(Regexes.TrimLeftSlashes, "").replace(Regexes.TrimRightSlashes, "");
|
||||
/** @ignore */
|
||||
export function getIdFromLink(resourceLink: string, isNameBased: boolean = true) {
|
||||
if (isNameBased) {
|
||||
resourceLink = trimSlashes(resourceLink);
|
||||
return resourceLink;
|
||||
} else {
|
||||
return parseLink(resourceLink).objectBody.id.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public static validateResourceId(resourceId: string) {
|
||||
// if resourceId is not a string or is empty throw an error
|
||||
if (typeof resourceId !== "string" || this.isStringNullOrEmpty(resourceId)) {
|
||||
throw new Error("Resource Id must be a string and cannot be undefined, null or empty");
|
||||
}
|
||||
|
||||
// if resourceId starts or ends with space throw an error
|
||||
if (resourceId[resourceId.length - 1] === " ") {
|
||||
throw new Error("Resource Id cannot end with space");
|
||||
}
|
||||
|
||||
// if resource id contains illegal characters throw an error
|
||||
if (Regexes.IllegalResourceIdCharacters.test(resourceId)) {
|
||||
throw new Error("Illegal characters ['/', '\\', '?', '#'] cannot be used in resourceId");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static getResourceIdFromPath(resourcePath: string) {
|
||||
if (!resourcePath || typeof resourcePath !== "string") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const trimmedPath = this.trimSlashFromLeftAndRight(resourcePath);
|
||||
const pathSegments = trimmedPath.split("/");
|
||||
|
||||
// number of segments of a path must always be even
|
||||
if (pathSegments.length % 2 !== 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return pathSegments[pathSegments.length - 1];
|
||||
}
|
||||
|
||||
public static parseConnectionPolicy(policy: any): ConnectionPolicy {
|
||||
if (!policy) {
|
||||
return new ConnectionPolicy();
|
||||
} else if (policy instanceof ConnectionPolicy) {
|
||||
return policy;
|
||||
/** @ignore */
|
||||
export function getPathFromLink(resourceLink: string, resourceType?: string, isNameBased: boolean = true) {
|
||||
if (isNameBased) {
|
||||
resourceLink = trimSlashes(resourceLink);
|
||||
if (resourceType) {
|
||||
return "/" + encodeURI(resourceLink) + "/" + resourceType;
|
||||
} else {
|
||||
const connectionPolicy = new ConnectionPolicy();
|
||||
for (const key of Object.getOwnPropertyNames(connectionPolicy)) {
|
||||
if ((policy as any)[key] !== undefined) {
|
||||
(connectionPolicy as any)[key] = (policy as any)[key];
|
||||
}
|
||||
}
|
||||
return connectionPolicy;
|
||||
return "/" + encodeURI(resourceLink);
|
||||
}
|
||||
} else {
|
||||
if (resourceType) {
|
||||
return "/" + resourceLink + resourceType + "/";
|
||||
} else {
|
||||
return "/" + resourceLink;
|
||||
}
|
||||
}
|
||||
}
|
||||
export function isStringNullOrEmpty(inputString: string) {
|
||||
// checks whether string is null, undefined, empty or only contains space
|
||||
return !inputString || /^\s*$/.test(inputString);
|
||||
}
|
||||
|
||||
export function trimSlashFromLeftAndRight(inputString: string) {
|
||||
if (typeof inputString !== "string") {
|
||||
throw new Error("invalid input: input is not string");
|
||||
}
|
||||
|
||||
return inputString.replace(Regexes.TrimLeftSlashes, "").replace(Regexes.TrimRightSlashes, "");
|
||||
}
|
||||
|
||||
export function validateResourceId(resourceId: string) {
|
||||
// if resourceId is not a string or is empty throw an error
|
||||
if (typeof resourceId !== "string" || isStringNullOrEmpty(resourceId)) {
|
||||
throw new Error("Resource Id must be a string and cannot be undefined, null or empty");
|
||||
}
|
||||
|
||||
// if resourceId starts or ends with space throw an error
|
||||
if (resourceId[resourceId.length - 1] === " ") {
|
||||
throw new Error("Resource Id cannot end with space");
|
||||
}
|
||||
|
||||
// if resource id contains illegal characters throw an error
|
||||
if (Regexes.IllegalResourceIdCharacters.test(resourceId)) {
|
||||
throw new Error("Illegal characters ['/', '\\', '?', '#'] cannot be used in resourceId");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getResourceIdFromPath(resourcePath: string) {
|
||||
if (!resourcePath || typeof resourcePath !== "string") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const trimmedPath = trimSlashFromLeftAndRight(resourcePath);
|
||||
const pathSegments = trimmedPath.split("/");
|
||||
|
||||
// number of segments of a path must always be even
|
||||
if (pathSegments.length % 2 !== 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return pathSegments[pathSegments.length - 1];
|
||||
}
|
||||
|
||||
export function parseConnectionPolicy(policy: any): ConnectionPolicy {
|
||||
if (!policy) {
|
||||
return new ConnectionPolicy();
|
||||
} else if (policy instanceof ConnectionPolicy) {
|
||||
return policy;
|
||||
} else {
|
||||
const connectionPolicy = new ConnectionPolicy();
|
||||
for (const key of Object.getOwnPropertyNames(connectionPolicy)) {
|
||||
if ((policy as any)[key] !== undefined) {
|
||||
(connectionPolicy as any)[key] = (policy as any)[key];
|
||||
}
|
||||
}
|
||||
return connectionPolicy;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,42 +3,40 @@ import { Constants } from "./index";
|
|||
|
||||
/** @hidden */
|
||||
|
||||
export class Platform {
|
||||
public static getPlatformDefaultHeaders(): { [key: string]: string } {
|
||||
const defaultHeaders: { [key: string]: string } = {};
|
||||
defaultHeaders[Constants.HttpHeaders.UserAgent] = Platform.getUserAgent();
|
||||
return defaultHeaders;
|
||||
}
|
||||
|
||||
public static getDecodedDataLength(encodedData: string): number {
|
||||
const buffer = Buffer.from(encodedData, "base64");
|
||||
return buffer.length;
|
||||
}
|
||||
|
||||
public static getUserAgent() {
|
||||
// gets the user agent in the following format
|
||||
// "{OSName}/{OSVersion} Nodejs/{NodejsVersion} documentdb-nodejs-sdk/{SDKVersion}"
|
||||
// for example:
|
||||
// "linux/3.4.0+ Nodejs/v0.10.25 documentdb-nodejs-sdk/1.10.0"
|
||||
// "win32/10.0.14393 Nodejs/v4.4.7 documentdb-nodejs-sdk/1.10.0"
|
||||
const osName = Platform._getSafeUserAgentSegmentInfo(os.platform());
|
||||
const osVersion = Platform._getSafeUserAgentSegmentInfo(os.release());
|
||||
const nodejsVersion = Platform._getSafeUserAgentSegmentInfo(process.version);
|
||||
|
||||
const userAgent = `${osName}/${osVersion} Nodejs/${nodejsVersion} ${Constants.SDKName}/${Constants.SDKVersion}`;
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
public static _getSafeUserAgentSegmentInfo(s: string) {
|
||||
// catch null, undefined, etc
|
||||
if (typeof s !== "string") {
|
||||
s = "unknown";
|
||||
}
|
||||
// remove all white spaces
|
||||
s = s.replace(/\s+/g, "");
|
||||
if (!s) {
|
||||
s = "unknown";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
export function getPlatformDefaultHeaders(): { [key: string]: string } {
|
||||
const defaultHeaders: { [key: string]: string } = {};
|
||||
defaultHeaders[Constants.HttpHeaders.UserAgent] = getUserAgent();
|
||||
return defaultHeaders;
|
||||
}
|
||||
|
||||
export function getDecodedDataLength(encodedData: string): number {
|
||||
const buffer = Buffer.from(encodedData, "base64");
|
||||
return buffer.length;
|
||||
}
|
||||
|
||||
export function getUserAgent() {
|
||||
// gets the user agent in the following format
|
||||
// "{OSName}/{OSVersion} Nodejs/{NodejsVersion} documentdb-nodejs-sdk/{SDKVersion}"
|
||||
// for example:
|
||||
// "linux/3.4.0+ Nodejs/v0.10.25 documentdb-nodejs-sdk/1.10.0"
|
||||
// "win32/10.0.14393 Nodejs/v4.4.7 documentdb-nodejs-sdk/1.10.0"
|
||||
const osName = getSafeUserAgentSegmentInfo(os.platform());
|
||||
const osVersion = getSafeUserAgentSegmentInfo(os.release());
|
||||
const nodejsVersion = getSafeUserAgentSegmentInfo(process.version);
|
||||
|
||||
const userAgent = `${osName}/${osVersion} Nodejs/${nodejsVersion} ${Constants.SDKName}/${Constants.SDKVersion}`;
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
export function getSafeUserAgentSegmentInfo(s: string) {
|
||||
// catch null, undefined, etc
|
||||
if (typeof s !== "string") {
|
||||
s = "unknown";
|
||||
}
|
||||
// remove all white spaces
|
||||
s = s.replace(/\s+/g, "");
|
||||
if (!s) {
|
||||
s = "unknown";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -1,226 +1,211 @@
|
|||
import { Constants, Helper } from "./index";
|
||||
import { trimSlashFromLeftAndRight, validateResourceId } from "./helper";
|
||||
import { Constants } from "./index";
|
||||
|
||||
/** @hidden */
|
||||
export class UriFactory {
|
||||
/**
|
||||
* Given a database id, this creates a database link.
|
||||
* @param {string} databaseId -The database id
|
||||
* @returns {string} -A database link in the format of dbs/{0} \
|
||||
* with {0} being a Uri escaped version of the databaseId
|
||||
* @description Would be used when creating or deleting a DocumentCollection \
|
||||
* or a User in Azure Cosmos DB database service
|
||||
*/
|
||||
public static createDatabaseUri(databaseId: string) {
|
||||
databaseId = Helper.trimSlashFromLeftAndRight(databaseId);
|
||||
Helper.validateResourceId(databaseId);
|
||||
/**
|
||||
* Given a database id, this creates a database link.
|
||||
* @param {string} databaseId -The database id
|
||||
* @returns {string} -A database link in the format of dbs/{0} \
|
||||
* with {0} being a Uri escaped version of the databaseId
|
||||
* @description Would be used when creating or deleting a DocumentCollection \
|
||||
* or a User in Azure Cosmos DB database service
|
||||
*/
|
||||
export function createDatabaseUri(databaseId: string) {
|
||||
databaseId = trimSlashFromLeftAndRight(databaseId);
|
||||
validateResourceId(databaseId);
|
||||
|
||||
return Constants.Path.DatabasesPathSegment + "/" + databaseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a database and collection id, this creates a collection link.
|
||||
* @param {string} databaseId -The database id
|
||||
* @param {string} collectionId -The collection id
|
||||
* @returns {string} A collection link in the format of dbs/{0}/colls/{1} \
|
||||
* with {0} being a Uri escaped version of the databaseId and {1} being collectionId
|
||||
* @description Would be used when updating or deleting a DocumentCollection, creating a \
|
||||
* Document, a StoredProcedure, a Trigger, a UserDefinedFunction, or when executing a query \
|
||||
* with CreateDocumentQuery in Azure Cosmos DB database service.
|
||||
*/
|
||||
public static createDocumentCollectionUri(databaseId: string, collectionId: string) {
|
||||
collectionId = Helper.trimSlashFromLeftAndRight(collectionId);
|
||||
Helper.validateResourceId(collectionId);
|
||||
|
||||
return this.createDatabaseUri(databaseId) + "/" + Constants.Path.CollectionsPathSegment + "/" + collectionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a database and user id, this creates a user link.
|
||||
* @param {string} databaseId -The database id
|
||||
* @param {string} userId -The user id
|
||||
* @returns {string} A user link in the format of dbs/{0}/users/{1} \
|
||||
* with {0} being a Uri escaped version of the databaseId and {1} being userId
|
||||
* @description Would be used when creating a Permission, or when replacing or deleting \
|
||||
* a User in Azure Cosmos DB database service
|
||||
*/
|
||||
public static createUserUri(databaseId: string, userId: string) {
|
||||
userId = Helper.trimSlashFromLeftAndRight(userId);
|
||||
Helper.validateResourceId(userId);
|
||||
|
||||
return this.createDatabaseUri(databaseId) + "/" + Constants.Path.UsersPathSegment + "/" + userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a database and collection id, this creates a collection link.
|
||||
* @param {string} databaseId -The database id
|
||||
* @param {string} collectionId -The collection id
|
||||
* @param {string} documentId -The document id
|
||||
* @returns {string} -A document link in the format of \
|
||||
* dbs/{0}/colls/{1}/docs/{2} with {0} being a Uri escaped version of \
|
||||
* the databaseId, {1} being collectionId and {2} being the documentId
|
||||
* @description Would be used when creating an Attachment, or when replacing \
|
||||
* or deleting a Document in Azure Cosmos DB database service
|
||||
*/
|
||||
public static createDocumentUri(databaseId: string, collectionId: string, documentId: string) {
|
||||
documentId = Helper.trimSlashFromLeftAndRight(documentId);
|
||||
Helper.validateResourceId(documentId);
|
||||
|
||||
return (
|
||||
this.createDocumentCollectionUri(databaseId, collectionId) +
|
||||
"/" +
|
||||
Constants.Path.DocumentsPathSegment +
|
||||
"/" +
|
||||
documentId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a database, collection and document id, this creates a document link.
|
||||
* @param {string} databaseId -The database Id
|
||||
* @param {string} userId -The user Id
|
||||
* @param {string} permissionId - The permissionId
|
||||
* @returns {string} A permission link in the format of dbs/{0}/users/{1}/permissions/{2} \
|
||||
* with {0} being a Uri escaped version of the databaseId, {1} being userId and {2} being permissionId
|
||||
* @description Would be used when replacing or deleting a Permission in Azure Cosmos DB database service.
|
||||
*/
|
||||
public static createPermissionUri(databaseId: string, userId: string, permissionId: string) {
|
||||
permissionId = Helper.trimSlashFromLeftAndRight(permissionId);
|
||||
Helper.validateResourceId(permissionId);
|
||||
|
||||
return this.createUserUri(databaseId, userId) + "/" + Constants.Path.PermissionsPathSegment + "/" + permissionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a database, collection and stored proc id, this creates a stored proc link.
|
||||
* @param {string} databaseId -The database Id
|
||||
* @param {string} collectionId -The collection Id
|
||||
* @param {string} storedProcedureId -The stored procedure Id
|
||||
* @returns {string} -A stored procedure link in the format of \
|
||||
* dbs/{0}/colls/{1}/sprocs/{2} with {0} being a Uri escaped version of the databaseId, \
|
||||
* {1} being collectionId and {2} being the storedProcedureId
|
||||
* @description Would be used when replacing, executing, or deleting a StoredProcedure in \
|
||||
* Azure Cosmos DB database service.
|
||||
*/
|
||||
public static createStoredProcedureUri(databaseId: string, collectionId: string, storedProcedureId: string) {
|
||||
storedProcedureId = Helper.trimSlashFromLeftAndRight(storedProcedureId);
|
||||
Helper.validateResourceId(storedProcedureId);
|
||||
|
||||
return (
|
||||
UriFactory.createDocumentCollectionUri(databaseId, collectionId) +
|
||||
"/" +
|
||||
Constants.Path.StoredProceduresPathSegment +
|
||||
"/" +
|
||||
storedProcedureId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Given a database, collection and trigger id, this creates a trigger link.
|
||||
* @param {string} databaseId -The database Id
|
||||
* @param {string} collectionId -The collection Id
|
||||
* @param {string} triggerId -The trigger Id
|
||||
* @returns {string} -A trigger link in the format of \
|
||||
* dbs/{0}/colls/{1}/triggers/{2} with {0} being a Uri escaped version of the databaseId, \
|
||||
* {1} being collectionId and {2} being the triggerId
|
||||
* @description Would be used when replacing, executing, or deleting a Trigger in Azure Cosmos DB database service
|
||||
*/
|
||||
public static createTriggerUri(databaseId: string, collectionId: string, triggerId: string) {
|
||||
triggerId = Helper.trimSlashFromLeftAndRight(triggerId);
|
||||
Helper.validateResourceId(triggerId);
|
||||
|
||||
return (
|
||||
this.createDocumentCollectionUri(databaseId, collectionId) +
|
||||
"/" +
|
||||
Constants.Path.TriggersPathSegment +
|
||||
"/" +
|
||||
triggerId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Given a database, collection and udf id, this creates a udf link.
|
||||
* @param {string} databaseId -The database Id
|
||||
* @param {string} collectionId -The collection Id
|
||||
* @param {string} udfId -The User Defined Function Id
|
||||
* @returns {string} -A udf link in the format of dbs/{0}/colls/{1}/udfs/{2} \
|
||||
* with {0} being a Uri escaped version of the databaseId, {1} being collectionId and {2} being the udfId
|
||||
* @description Would be used when replacing, executing, or deleting a UserDefinedFunction in \
|
||||
* Azure Cosmos DB database service
|
||||
*/
|
||||
public static createUserDefinedFunctionUri(databaseId: string, collectionId: string, udfId: string) {
|
||||
udfId = Helper.trimSlashFromLeftAndRight(udfId);
|
||||
Helper.validateResourceId(udfId);
|
||||
|
||||
return (
|
||||
this.createDocumentCollectionUri(databaseId, collectionId) +
|
||||
"/" +
|
||||
Constants.Path.UserDefinedFunctionsPathSegment +
|
||||
"/" +
|
||||
udfId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Given a database, collection and conflict id, this creates a conflict link.
|
||||
* @param {string} databaseId -The database Id
|
||||
* @param {string} collectionId -The collection Id
|
||||
* @param {string} conflictId -The conflict Id
|
||||
* @returns {string} -A conflict link in the format of dbs/{0}/colls/{1}/conflicts/{2} \
|
||||
* with {0} being a Uri escaped version of the databaseId, {1} being collectionId and {2} being the conflictId
|
||||
* @description Would be used when creating a Conflict in Azure Cosmos DB database service.
|
||||
*/
|
||||
public static createConflictUri(databaseId: string, collectionId: string, conflictId: string) {
|
||||
conflictId = Helper.trimSlashFromLeftAndRight(conflictId);
|
||||
Helper.validateResourceId(conflictId);
|
||||
|
||||
return (
|
||||
this.createDocumentCollectionUri(databaseId, collectionId) +
|
||||
"/" +
|
||||
Constants.Path.ConflictsPathSegment +
|
||||
"/" +
|
||||
conflictId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Given a database, collection and conflict id, this creates a conflict link.
|
||||
* @param {string} databaseId -The database Id
|
||||
* @param {string} collectionId -The collection Id
|
||||
* @param {string} documentId -The document Id\
|
||||
* @param {string} attachmentId -The attachment Id
|
||||
* @returns {string} -A conflict link in the format of dbs/{0}/colls/{1}/conflicts/{2} \
|
||||
* with {0} being a Uri escaped version of the databaseId, {1} being collectionId and {2} being the conflictId
|
||||
* @description Would be used when creating a Conflict in Azure Cosmos DB database service.
|
||||
*/
|
||||
public static createAttachmentUri(
|
||||
databaseId: string,
|
||||
collectionId: string,
|
||||
documentId: string,
|
||||
attachmentId: string
|
||||
) {
|
||||
attachmentId = Helper.trimSlashFromLeftAndRight(attachmentId);
|
||||
Helper.validateResourceId(attachmentId);
|
||||
|
||||
return (
|
||||
this.createDocumentUri(databaseId, collectionId, documentId) +
|
||||
"/" +
|
||||
Constants.Path.AttachmentsPathSegment +
|
||||
"/" +
|
||||
attachmentId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Given a database and collection, this creates a partition key ranges link in\
|
||||
* the Azure Cosmos DB database service.
|
||||
* @param {string} databaseId -The database Id
|
||||
* @param {string} collectionId -The collection Id
|
||||
* @returns {string} -A partition key ranges link in the format of \
|
||||
* dbs/{0}/colls/{1}/pkranges with {0} being a Uri escaped version of the databaseId and {1} being collectionId
|
||||
*/
|
||||
public static createPartitionKeyRangesUri(databaseId: string, collectionId: string) {
|
||||
return (
|
||||
this.createDocumentCollectionUri(databaseId, collectionId) + "/" + Constants.Path.PartitionKeyRangesPathSegment
|
||||
);
|
||||
}
|
||||
return Constants.Path.DatabasesPathSegment + "/" + databaseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a database and collection id, this creates a collection link.
|
||||
* @param {string} databaseId -The database id
|
||||
* @param {string} collectionId -The collection id
|
||||
* @returns {string} A collection link in the format of dbs/{0}/colls/{1} \
|
||||
* with {0} being a Uri escaped version of the databaseId and {1} being collectionId
|
||||
* @description Would be used when updating or deleting a DocumentCollection, creating a \
|
||||
* Document, a StoredProcedure, a Trigger, a UserDefinedFunction, or when executing a query \
|
||||
* with CreateDocumentQuery in Azure Cosmos DB database service.
|
||||
*/
|
||||
export function createDocumentCollectionUri(databaseId: string, collectionId: string) {
|
||||
collectionId = trimSlashFromLeftAndRight(collectionId);
|
||||
validateResourceId(collectionId);
|
||||
|
||||
return createDatabaseUri(databaseId) + "/" + Constants.Path.CollectionsPathSegment + "/" + collectionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a database and user id, this creates a user link.
|
||||
* @param {string} databaseId -The database id
|
||||
* @param {string} userId -The user id
|
||||
* @returns {string} A user link in the format of dbs/{0}/users/{1} \
|
||||
* with {0} being a Uri escaped version of the databaseId and {1} being userId
|
||||
* @description Would be used when creating a Permission, or when replacing or deleting \
|
||||
* a User in Azure Cosmos DB database service
|
||||
*/
|
||||
export function createUserUri(databaseId: string, userId: string) {
|
||||
userId = trimSlashFromLeftAndRight(userId);
|
||||
validateResourceId(userId);
|
||||
|
||||
return createDatabaseUri(databaseId) + "/" + Constants.Path.UsersPathSegment + "/" + userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a database and collection id, this creates a collection link.
|
||||
* @param {string} databaseId -The database id
|
||||
* @param {string} collectionId -The collection id
|
||||
* @param {string} documentId -The document id
|
||||
* @returns {string} -A document link in the format of \
|
||||
* dbs/{0}/colls/{1}/docs/{2} with {0} being a Uri escaped version of \
|
||||
* the databaseId, {1} being collectionId and {2} being the documentId
|
||||
* @description Would be used when creating an Attachment, or when replacing \
|
||||
* or deleting a Document in Azure Cosmos DB database service
|
||||
*/
|
||||
export function createDocumentUri(databaseId: string, collectionId: string, documentId: string) {
|
||||
documentId = trimSlashFromLeftAndRight(documentId);
|
||||
validateResourceId(documentId);
|
||||
|
||||
return (
|
||||
createDocumentCollectionUri(databaseId, collectionId) + "/" + Constants.Path.DocumentsPathSegment + "/" + documentId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a database, collection and document id, this creates a document link.
|
||||
* @param {string} databaseId -The database Id
|
||||
* @param {string} userId -The user Id
|
||||
* @param {string} permissionId - The permissionId
|
||||
* @returns {string} A permission link in the format of dbs/{0}/users/{1}/permissions/{2} \
|
||||
* with {0} being a Uri escaped version of the databaseId, {1} being userId and {2} being permissionId
|
||||
* @description Would be used when replacing or deleting a Permission in Azure Cosmos DB database service.
|
||||
*/
|
||||
export function createPermissionUri(databaseId: string, userId: string, permissionId: string) {
|
||||
permissionId = trimSlashFromLeftAndRight(permissionId);
|
||||
validateResourceId(permissionId);
|
||||
|
||||
return createUserUri(databaseId, userId) + "/" + Constants.Path.PermissionsPathSegment + "/" + permissionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a database, collection and stored proc id, this creates a stored proc link.
|
||||
* @param {string} databaseId -The database Id
|
||||
* @param {string} collectionId -The collection Id
|
||||
* @param {string} storedProcedureId -The stored procedure Id
|
||||
* @returns {string} -A stored procedure link in the format of \
|
||||
* dbs/{0}/colls/{1}/sprocs/{2} with {0} being a Uri escaped version of the databaseId, \
|
||||
* {1} being collectionId and {2} being the storedProcedureId
|
||||
* @description Would be used when replacing, executing, or deleting a StoredProcedure in \
|
||||
* Azure Cosmos DB database service.
|
||||
*/
|
||||
export function createStoredProcedureUri(databaseId: string, collectionId: string, storedProcedureId: string) {
|
||||
storedProcedureId = trimSlashFromLeftAndRight(storedProcedureId);
|
||||
validateResourceId(storedProcedureId);
|
||||
|
||||
return (
|
||||
createDocumentCollectionUri(databaseId, collectionId) +
|
||||
"/" +
|
||||
Constants.Path.StoredProceduresPathSegment +
|
||||
"/" +
|
||||
storedProcedureId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Given a database, collection and trigger id, this creates a trigger link.
|
||||
* @param {string} databaseId -The database Id
|
||||
* @param {string} collectionId -The collection Id
|
||||
* @param {string} triggerId -The trigger Id
|
||||
* @returns {string} -A trigger link in the format of \
|
||||
* dbs/{0}/colls/{1}/triggers/{2} with {0} being a Uri escaped version of the databaseId, \
|
||||
* {1} being collectionId and {2} being the triggerId
|
||||
* @description Would be used when replacing, executing, or deleting a Trigger in Azure Cosmos DB database service
|
||||
*/
|
||||
export function createTriggerUri(databaseId: string, collectionId: string, triggerId: string) {
|
||||
triggerId = trimSlashFromLeftAndRight(triggerId);
|
||||
validateResourceId(triggerId);
|
||||
|
||||
return (
|
||||
createDocumentCollectionUri(databaseId, collectionId) + "/" + Constants.Path.TriggersPathSegment + "/" + triggerId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Given a database, collection and udf id, this creates a udf link.
|
||||
* @param {string} databaseId -The database Id
|
||||
* @param {string} collectionId -The collection Id
|
||||
* @param {string} udfId -The User Defined Function Id
|
||||
* @returns {string} -A udf link in the format of dbs/{0}/colls/{1}/udfs/{2} \
|
||||
* with {0} being a Uri escaped version of the databaseId, {1} being collectionId and {2} being the udfId
|
||||
* @description Would be used when replacing, executing, or deleting a UserDefinedFunction in \
|
||||
* Azure Cosmos DB database service
|
||||
*/
|
||||
export function createUserDefinedFunctionUri(databaseId: string, collectionId: string, udfId: string) {
|
||||
udfId = trimSlashFromLeftAndRight(udfId);
|
||||
validateResourceId(udfId);
|
||||
|
||||
return (
|
||||
createDocumentCollectionUri(databaseId, collectionId) +
|
||||
"/" +
|
||||
Constants.Path.UserDefinedFunctionsPathSegment +
|
||||
"/" +
|
||||
udfId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Given a database, collection and conflict id, this creates a conflict link.
|
||||
* @param {string} databaseId -The database Id
|
||||
* @param {string} collectionId -The collection Id
|
||||
* @param {string} conflictId -The conflict Id
|
||||
* @returns {string} -A conflict link in the format of dbs/{0}/colls/{1}/conflicts/{2} \
|
||||
* with {0} being a Uri escaped version of the databaseId, {1} being collectionId and {2} being the conflictId
|
||||
* @description Would be used when creating a Conflict in Azure Cosmos DB database service.
|
||||
*/
|
||||
export function createConflictUri(databaseId: string, collectionId: string, conflictId: string) {
|
||||
conflictId = trimSlashFromLeftAndRight(conflictId);
|
||||
validateResourceId(conflictId);
|
||||
|
||||
return (
|
||||
createDocumentCollectionUri(databaseId, collectionId) + "/" + Constants.Path.ConflictsPathSegment + "/" + conflictId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Given a database, collection and conflict id, this creates a conflict link.
|
||||
* @param {string} databaseId -The database Id
|
||||
* @param {string} collectionId -The collection Id
|
||||
* @param {string} documentId -The document Id\
|
||||
* @param {string} attachmentId -The attachment Id
|
||||
* @returns {string} -A conflict link in the format of dbs/{0}/colls/{1}/conflicts/{2} \
|
||||
* with {0} being a Uri escaped version of the databaseId, {1} being collectionId and {2} being the conflictId
|
||||
* @description Would be used when creating a Conflict in Azure Cosmos DB database service.
|
||||
*/
|
||||
export function createAttachmentUri(
|
||||
databaseId: string,
|
||||
collectionId: string,
|
||||
documentId: string,
|
||||
attachmentId: string
|
||||
) {
|
||||
attachmentId = trimSlashFromLeftAndRight(attachmentId);
|
||||
validateResourceId(attachmentId);
|
||||
|
||||
return (
|
||||
createDocumentUri(databaseId, collectionId, documentId) +
|
||||
"/" +
|
||||
Constants.Path.AttachmentsPathSegment +
|
||||
"/" +
|
||||
attachmentId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Given a database and collection, this creates a partition key ranges link in\
|
||||
* the Azure Cosmos DB database service.
|
||||
* @param {string} databaseId -The database Id
|
||||
* @param {string} collectionId -The collection Id
|
||||
* @returns {string} -A partition key ranges link in the format of \
|
||||
* dbs/{0}/colls/{1}/pkranges with {0} being a Uri escaped version of the databaseId and {1} being collectionId
|
||||
*/
|
||||
export function createPartitionKeyRangesUri(databaseId: string, collectionId: string) {
|
||||
return createDocumentCollectionUri(databaseId, collectionId) + "/" + Constants.Path.PartitionKeyRangesPathSegment;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as url from "url";
|
||||
import { Constants, Helper } from "./common";
|
||||
import { Constants, sleep } from "./common";
|
||||
import { CosmosClient } from "./CosmosClient";
|
||||
import { CosmosClientOptions } from "./CosmosClientOptions";
|
||||
import { DatabaseAccount } from "./documents";
|
||||
|
@ -136,7 +136,7 @@ export class GlobalEndpointManager {
|
|||
if (!shouldRefresh) {
|
||||
break;
|
||||
}
|
||||
await Helper.sleep(this.backgroundRefreshTimeIntervalInMS);
|
||||
await sleep(this.backgroundRefreshTimeIntervalInMS);
|
||||
} while (shouldRefresh);
|
||||
} catch (err) {
|
||||
/* swallow error */
|
||||
|
|
|
@ -21,7 +21,7 @@ export {
|
|||
UserDefinedFunctionType
|
||||
} from "./documents";
|
||||
|
||||
export { Constants, UriFactory } from "./common";
|
||||
export { Constants } from "./common";
|
||||
export { RetryOptions } from "./retry";
|
||||
export { Response, RequestOptions, FeedOptions, MediaOptions, ErrorResponse } from "./request";
|
||||
export { IHeaders, SqlParameter, SqlQuerySpec } from "./queryExecutionContext";
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { ClientContext } from "../ClientContext";
|
||||
import { Constants, Helper, StatusCodes, SubStatusCodes } from "../common";
|
||||
import { Constants, getIdFromLink, getPathFromLink, StatusCodes, SubStatusCodes } from "../common";
|
||||
import { FeedOptions } from "../request";
|
||||
import { Response } from "../request/request";
|
||||
import { DefaultQueryExecutionContext } from "./defaultQueryExecutionContext";
|
||||
import { FetchResult, FetchResultType } from "./FetchResult";
|
||||
import { HeaderUtils, IHeaders } from "./headerUtils";
|
||||
import { getInitialHeader, IHeaders, mergeHeaders } from "./headerUtils";
|
||||
import { FetchFunctionCallback, SqlQuerySpec } from "./index";
|
||||
|
||||
/** @hidden */
|
||||
|
@ -62,7 +62,7 @@ export class DocumentProducer {
|
|||
|
||||
this.previousContinuationToken = undefined;
|
||||
this.continuationToken = undefined;
|
||||
this.respHeaders = HeaderUtils.getInitialHeader();
|
||||
this.respHeaders = getInitialHeader();
|
||||
|
||||
// tslint:disable-next-line:no-shadowed-variable
|
||||
this.internalExecutionContext = new DefaultQueryExecutionContext(clientContext, query, options, this.fetchFunction);
|
||||
|
@ -93,8 +93,8 @@ export class DocumentProducer {
|
|||
}
|
||||
|
||||
public fetchFunction: FetchFunctionCallback = async (options: any) => {
|
||||
const path = Helper.getPathFromLink(this.collectionLink, "docs");
|
||||
const id = Helper.getIdFromLink(this.collectionLink);
|
||||
const path = getPathFromLink(this.collectionLink, "docs");
|
||||
const id = getIdFromLink(this.collectionLink);
|
||||
|
||||
return this.clientContext.queryFeed(
|
||||
path,
|
||||
|
@ -124,7 +124,7 @@ export class DocumentProducer {
|
|||
|
||||
private _getAndResetActiveResponseHeaders() {
|
||||
const ret = this.respHeaders;
|
||||
this.respHeaders = HeaderUtils.getInitialHeader();
|
||||
this.respHeaders = getInitialHeader();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -294,7 +294,7 @@ export class DocumentProducer {
|
|||
if (result === undefined) {
|
||||
return { result: undefined, headers };
|
||||
}
|
||||
HeaderUtils.mergeHeaders(this.respHeaders, headers);
|
||||
mergeHeaders(this.respHeaders, headers);
|
||||
|
||||
return this.current();
|
||||
} catch (err) {
|
||||
|
|
|
@ -7,63 +7,61 @@ export interface IHeaders {
|
|||
|
||||
/** @hidden */
|
||||
// TODO: docs
|
||||
export class HeaderUtils {
|
||||
public static getRequestChargeIfAny(headers: IHeaders): number {
|
||||
if (typeof headers === "number") {
|
||||
return headers;
|
||||
} else if (typeof headers === "string") {
|
||||
return parseFloat(headers);
|
||||
}
|
||||
export function getRequestChargeIfAny(headers: IHeaders): number {
|
||||
if (typeof headers === "number") {
|
||||
return headers;
|
||||
} else if (typeof headers === "string") {
|
||||
return parseFloat(headers);
|
||||
}
|
||||
|
||||
if (headers) {
|
||||
const rc = headers[Constants.HttpHeaders.RequestCharge];
|
||||
if (rc) {
|
||||
return parseFloat(rc as string);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
if (headers) {
|
||||
const rc = headers[Constants.HttpHeaders.RequestCharge];
|
||||
if (rc) {
|
||||
return parseFloat(rc as string);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static getInitialHeader(): IHeaders {
|
||||
const headers: IHeaders = {};
|
||||
export function getInitialHeader(): IHeaders {
|
||||
const headers: IHeaders = {};
|
||||
headers[Constants.HttpHeaders.RequestCharge] = 0;
|
||||
headers[Constants.HttpHeaders.QueryMetrics] = {};
|
||||
return headers;
|
||||
}
|
||||
|
||||
// TODO: The name of this method isn't very accurate to what it does
|
||||
export function mergeHeaders(headers: IHeaders, toBeMergedHeaders: IHeaders) {
|
||||
if (headers[Constants.HttpHeaders.RequestCharge] === undefined) {
|
||||
headers[Constants.HttpHeaders.RequestCharge] = 0;
|
||||
headers[Constants.HttpHeaders.QueryMetrics] = {};
|
||||
return headers;
|
||||
}
|
||||
|
||||
// TODO: The name of this method isn't very accurate to what it does
|
||||
public static mergeHeaders(headers: IHeaders, toBeMergedHeaders: IHeaders) {
|
||||
if (headers[Constants.HttpHeaders.RequestCharge] === undefined) {
|
||||
headers[Constants.HttpHeaders.RequestCharge] = 0;
|
||||
}
|
||||
if (headers[Constants.HttpHeaders.QueryMetrics] === undefined) {
|
||||
headers[Constants.HttpHeaders.QueryMetrics] = QueryMetrics.zero;
|
||||
}
|
||||
|
||||
if (headers[Constants.HttpHeaders.QueryMetrics] === undefined) {
|
||||
headers[Constants.HttpHeaders.QueryMetrics] = QueryMetrics.zero;
|
||||
}
|
||||
if (!toBeMergedHeaders) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!toBeMergedHeaders) {
|
||||
return;
|
||||
}
|
||||
(headers[Constants.HttpHeaders.RequestCharge] as number) += getRequestChargeIfAny(toBeMergedHeaders);
|
||||
if (toBeMergedHeaders[Constants.HttpHeaders.IsRUPerMinuteUsed]) {
|
||||
headers[Constants.HttpHeaders.IsRUPerMinuteUsed] = toBeMergedHeaders[Constants.HttpHeaders.IsRUPerMinuteUsed];
|
||||
}
|
||||
|
||||
(headers[Constants.HttpHeaders.RequestCharge] as number) += HeaderUtils.getRequestChargeIfAny(toBeMergedHeaders);
|
||||
if (toBeMergedHeaders[Constants.HttpHeaders.IsRUPerMinuteUsed]) {
|
||||
headers[Constants.HttpHeaders.IsRUPerMinuteUsed] = toBeMergedHeaders[Constants.HttpHeaders.IsRUPerMinuteUsed];
|
||||
}
|
||||
if (Constants.HttpHeaders.QueryMetrics in toBeMergedHeaders) {
|
||||
const headerQueryMetrics = headers[Constants.HttpHeaders.QueryMetrics];
|
||||
const toBeMergedHeaderQueryMetrics = toBeMergedHeaders[Constants.HttpHeaders.QueryMetrics];
|
||||
|
||||
if (Constants.HttpHeaders.QueryMetrics in toBeMergedHeaders) {
|
||||
const headerQueryMetrics = headers[Constants.HttpHeaders.QueryMetrics];
|
||||
const toBeMergedHeaderQueryMetrics = toBeMergedHeaders[Constants.HttpHeaders.QueryMetrics];
|
||||
|
||||
for (const partitionId in toBeMergedHeaderQueryMetrics) {
|
||||
if (partitionId in headerQueryMetrics) {
|
||||
const combinedQueryMetrics = headerQueryMetrics[partitionId].add(toBeMergedHeaderQueryMetrics[partitionId]);
|
||||
headerQueryMetrics[partitionId] = combinedQueryMetrics;
|
||||
} else {
|
||||
headerQueryMetrics[partitionId] = toBeMergedHeaderQueryMetrics[partitionId];
|
||||
}
|
||||
for (const partitionId in toBeMergedHeaderQueryMetrics) {
|
||||
if (partitionId in headerQueryMetrics) {
|
||||
const combinedQueryMetrics = headerQueryMetrics[partitionId].add(toBeMergedHeaderQueryMetrics[partitionId]);
|
||||
headerQueryMetrics[partitionId] = combinedQueryMetrics;
|
||||
} else {
|
||||
headerQueryMetrics[partitionId] = toBeMergedHeaderQueryMetrics[partitionId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
import * as bs from "binary-search-bounds";
|
||||
import * as bs from "binary-search-bounds";
|
||||
import PriorityQueue from "priorityqueuejs";
|
||||
import semaphore from "semaphore";
|
||||
import { ClientContext } from "../ClientContext";
|
||||
import { StatusCodes, SubStatusCodes } from "../common";
|
||||
import { Response } from "../request/request";
|
||||
import { PARITIONKEYRANGE, QueryRange, SmartRoutingMapProvider } from "../routing";
|
||||
import {
|
||||
DocumentProducer,
|
||||
HeaderUtils,
|
||||
IExecutionContext,
|
||||
IHeaders,
|
||||
PartitionedQueryExecutionContextInfo,
|
||||
PartitionedQueryExecutionContextInfoParser
|
||||
} from "./index";
|
||||
import { getInitialHeader, mergeHeaders } from "./headerUtils";
|
||||
import { DocumentProducer, IExecutionContext, IHeaders, PartitionedQueryExecutionContextInfo } from "./index";
|
||||
import * as PartitionedQueryExecutionContextInfoParser from "./partitionedQueryExecutionContextInfoParser";
|
||||
|
||||
/** @hidden */
|
||||
export enum ParallelQueryExecutionContextBaseStates {
|
||||
|
@ -77,7 +72,7 @@ export abstract class ParallelQueryExecutionContextBase implements IExecutionCon
|
|||
|
||||
this.requestContinuation = options ? options.continuation : null;
|
||||
// response headers of undergoing operation
|
||||
this.respHeaders = HeaderUtils.getInitialHeader();
|
||||
this.respHeaders = getInitialHeader();
|
||||
|
||||
// Make priority queue for documentProducers
|
||||
// The comparator is supplied by the derived class
|
||||
|
@ -219,12 +214,12 @@ export abstract class ParallelQueryExecutionContextBase implements IExecutionCon
|
|||
}
|
||||
|
||||
private _mergeWithActiveResponseHeaders(headers: IHeaders) {
|
||||
HeaderUtils.mergeHeaders(this.respHeaders, headers);
|
||||
mergeHeaders(this.respHeaders, headers);
|
||||
}
|
||||
|
||||
private _getAndResetActiveResponseHeaders() {
|
||||
const ret = this.respHeaders;
|
||||
this.respHeaders = HeaderUtils.getInitialHeader();
|
||||
this.respHeaders = getInitialHeader();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,37 +14,34 @@ export interface PartitionedQueryExecutionContextInfo {
|
|||
}
|
||||
|
||||
// TODO: any partitionedQueryExecutionInfo
|
||||
/** @hidden */
|
||||
export class PartitionedQueryExecutionContextInfoParser {
|
||||
public static parseRewrittenQuery(partitionedQueryExecutionInfo: { [key: string]: any }) {
|
||||
return this._extract(partitionedQueryExecutionInfo, PartitionedQueryContants.RewrittenQueryPath);
|
||||
}
|
||||
public static parseQueryRanges(partitionedQueryExecutionInfo: { [key: string]: any }) {
|
||||
return this._extract(partitionedQueryExecutionInfo, PartitionedQueryContants.QueryRangesPath);
|
||||
}
|
||||
public static parseOrderBy(partitionedQueryExecutionInfo: { [key: string]: any }) {
|
||||
return this._extract(partitionedQueryExecutionInfo, PartitionedQueryContants.OrderByPath);
|
||||
}
|
||||
public static parseAggregates(partitionedQueryExecutionInfo: { [key: string]: any }) {
|
||||
return this._extract(partitionedQueryExecutionInfo, PartitionedQueryContants.AggregatePath);
|
||||
}
|
||||
public static parseTop(partitionedQueryExecutionInfo: { [key: string]: any }) {
|
||||
return this._extract(partitionedQueryExecutionInfo, PartitionedQueryContants.TopPath);
|
||||
}
|
||||
private static _extract(partitionedQueryExecutionInfo: { [key: string]: any }, path: string | string[]) {
|
||||
let item = partitionedQueryExecutionInfo;
|
||||
if (typeof path === "string") {
|
||||
return item[path];
|
||||
}
|
||||
if (!Array.isArray(path)) {
|
||||
throw new Error(`JSON.stringify(path is expected to be an array`);
|
||||
}
|
||||
for (const p of path) {
|
||||
item = item[p];
|
||||
if (item === undefined) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
export function parseRewrittenQuery(partitionedQueryExecutionInfo: { [key: string]: any }) {
|
||||
return _extract(partitionedQueryExecutionInfo, PartitionedQueryContants.RewrittenQueryPath);
|
||||
}
|
||||
export function parseQueryRanges(partitionedQueryExecutionInfo: { [key: string]: any }) {
|
||||
return _extract(partitionedQueryExecutionInfo, PartitionedQueryContants.QueryRangesPath);
|
||||
}
|
||||
export function parseOrderBy(partitionedQueryExecutionInfo: { [key: string]: any }) {
|
||||
return _extract(partitionedQueryExecutionInfo, PartitionedQueryContants.OrderByPath);
|
||||
}
|
||||
export function parseAggregates(partitionedQueryExecutionInfo: { [key: string]: any }) {
|
||||
return _extract(partitionedQueryExecutionInfo, PartitionedQueryContants.AggregatePath);
|
||||
}
|
||||
export function parseTop(partitionedQueryExecutionInfo: { [key: string]: any }) {
|
||||
return _extract(partitionedQueryExecutionInfo, PartitionedQueryContants.TopPath);
|
||||
}
|
||||
function _extract(partitionedQueryExecutionInfo: { [key: string]: any }, path: string | string[]) {
|
||||
let item = partitionedQueryExecutionInfo;
|
||||
if (typeof path === "string") {
|
||||
return item[path];
|
||||
}
|
||||
if (!Array.isArray(path)) {
|
||||
throw new Error(`JSON.stringify(path is expected to be an array`);
|
||||
}
|
||||
for (const p of path) {
|
||||
item = item[p];
|
||||
if (item === undefined) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
|
|
@ -6,15 +6,15 @@ import {
|
|||
OrderByEndpointComponent,
|
||||
TopEndpointComponent
|
||||
} from "./EndpointComponent";
|
||||
import { getInitialHeader, mergeHeaders } from "./headerUtils";
|
||||
import {
|
||||
HeaderUtils,
|
||||
IExecutionContext,
|
||||
IHeaders,
|
||||
OrderByQueryExecutionContext,
|
||||
ParallelQueryExecutionContext,
|
||||
PartitionedQueryExecutionContextInfo,
|
||||
PartitionedQueryExecutionContextInfoParser
|
||||
PartitionedQueryExecutionContextInfo
|
||||
} from "./index";
|
||||
import * as PartitionedQueryExecutionContextInfoParser from "./partitionedQueryExecutionContextInfoParser";
|
||||
|
||||
/** @hidden */
|
||||
export class PipelinedQueryExecutionContext implements IExecutionContext {
|
||||
|
@ -93,7 +93,7 @@ export class PipelinedQueryExecutionContext implements IExecutionContext {
|
|||
return this.endpoint.fetchMore();
|
||||
} else {
|
||||
this.fetchBuffer = [];
|
||||
this.fetchMoreRespHeaders = HeaderUtils.getInitialHeader();
|
||||
this.fetchMoreRespHeaders = getInitialHeader();
|
||||
return this._fetchMoreImplementation();
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ export class PipelinedQueryExecutionContext implements IExecutionContext {
|
|||
private async _fetchMoreImplementation(): Promise<Response<any>> {
|
||||
try {
|
||||
const { result: item, headers } = await this.endpoint.nextItem();
|
||||
HeaderUtils.mergeHeaders(this.fetchMoreRespHeaders, headers);
|
||||
mergeHeaders(this.fetchMoreRespHeaders, headers);
|
||||
if (item === undefined) {
|
||||
// no more results
|
||||
if (this.fetchBuffer.length === 0) {
|
||||
|
@ -130,7 +130,7 @@ export class PipelinedQueryExecutionContext implements IExecutionContext {
|
|||
}
|
||||
}
|
||||
} catch (err) {
|
||||
HeaderUtils.mergeHeaders(this.fetchMoreRespHeaders, err.headers);
|
||||
mergeHeaders(this.fetchMoreRespHeaders, err.headers);
|
||||
err.headers = this.fetchMoreRespHeaders;
|
||||
if (err) {
|
||||
throw err;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { QueryMetricsUtils } from "./queryMetricsUtils";
|
||||
|
||||
export class ClientSideMetrics {
|
||||
constructor(public readonly requestCharge: number) {}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
export { ClientSideMetrics } from "./clientSideMetrics";
|
||||
export { QueryMetrics } from "./queryMetrics";
|
||||
export { default as QueryMetricsConstants } from "./queryMetricsConstants";
|
||||
export { QueryMetricsUtils } from "./queryMetricsUtils";
|
||||
export { QueryPreparationTimes } from "./queryPreparationTime";
|
||||
export { RuntimeExecutionTimes } from "./runtimeExecutionTimes";
|
||||
export { TimeSpan } from "./timeSpan";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ClientSideMetrics } from "./clientSideMetrics";
|
||||
import QueryMetricsConstants from "./queryMetricsConstants";
|
||||
import { QueryMetricsUtils } from "./queryMetricsUtils";
|
||||
import { parseDelimitedString, timeSpanFromMetrics } from "./queryMetricsUtils";
|
||||
import { QueryPreparationTimes } from "./queryPreparationTime";
|
||||
import { RuntimeExecutionTimes } from "./runtimeExecutionTimes";
|
||||
import { TimeSpan } from "./timeSpan";
|
||||
|
@ -182,7 +182,7 @@ export class QueryMetrics {
|
|||
* @instance
|
||||
*/
|
||||
public static createFromDelimitedString(delimitedString: string, clientSideMetrics?: ClientSideMetrics) {
|
||||
const metrics = QueryMetricsUtils.parseDelimitedString(delimitedString);
|
||||
const metrics = parseDelimitedString(delimitedString);
|
||||
|
||||
const indexHitRatio = metrics[QueryMetricsConstants.IndexHitRatio] || 0;
|
||||
const retrievedDocumentCount = metrics[QueryMetricsConstants.RetrievedDocumentCount] || 0;
|
||||
|
@ -190,10 +190,7 @@ export class QueryMetrics {
|
|||
const outputDocumentCount = metrics[QueryMetricsConstants.OutputDocumentCount] || 0;
|
||||
const outputDocumentSize = metrics[QueryMetricsConstants.OutputDocumentSize] || 0;
|
||||
const retrievedDocumentSize = metrics[QueryMetricsConstants.RetrievedDocumentSize] || 0;
|
||||
const totalQueryExecutionTime = QueryMetricsUtils.timeSpanFromMetrics(
|
||||
metrics,
|
||||
QueryMetricsConstants.TotalQueryExecutionTimeInMs
|
||||
);
|
||||
const totalQueryExecutionTime = timeSpanFromMetrics(metrics, QueryMetricsConstants.TotalQueryExecutionTimeInMs);
|
||||
return new QueryMetrics(
|
||||
retrievedDocumentCount,
|
||||
retrievedDocumentSize,
|
||||
|
@ -202,11 +199,11 @@ export class QueryMetrics {
|
|||
indexHitCount,
|
||||
totalQueryExecutionTime,
|
||||
QueryPreparationTimes.createFromDelimitedString(delimitedString),
|
||||
QueryMetricsUtils.timeSpanFromMetrics(metrics, QueryMetricsConstants.IndexLookupTimeInMs),
|
||||
QueryMetricsUtils.timeSpanFromMetrics(metrics, QueryMetricsConstants.DocumentLoadTimeInMs),
|
||||
QueryMetricsUtils.timeSpanFromMetrics(metrics, QueryMetricsConstants.VMExecutionTimeInMs),
|
||||
timeSpanFromMetrics(metrics, QueryMetricsConstants.IndexLookupTimeInMs),
|
||||
timeSpanFromMetrics(metrics, QueryMetricsConstants.DocumentLoadTimeInMs),
|
||||
timeSpanFromMetrics(metrics, QueryMetricsConstants.VMExecutionTimeInMs),
|
||||
RuntimeExecutionTimes.createFromDelimitedString(delimitedString),
|
||||
QueryMetricsUtils.timeSpanFromMetrics(metrics, QueryMetricsConstants.DocumentWriteTimeInMs),
|
||||
timeSpanFromMetrics(metrics, QueryMetricsConstants.DocumentWriteTimeInMs),
|
||||
clientSideMetrics || ClientSideMetrics.zero
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,39 +1,37 @@
|
|||
import { TimeSpan } from "./timeSpan";
|
||||
|
||||
export class QueryMetricsUtils {
|
||||
public static parseDelimitedString(delimitedString: string) {
|
||||
if (delimitedString == null) {
|
||||
throw new Error("delimitedString is null or undefined");
|
||||
}
|
||||
|
||||
const metrics: { [key: string]: any } = {};
|
||||
|
||||
const headerAttributes = delimitedString.split(";");
|
||||
for (const attribute of headerAttributes) {
|
||||
const attributeKeyValue = attribute.split("=");
|
||||
|
||||
if (attributeKeyValue.length !== 2) {
|
||||
throw new Error("recieved a malformed delimited string");
|
||||
}
|
||||
|
||||
const attributeKey = attributeKeyValue[0];
|
||||
const attributeValue = parseFloat(attributeKeyValue[1]);
|
||||
|
||||
metrics[attributeKey] = attributeValue;
|
||||
}
|
||||
|
||||
return metrics;
|
||||
export function parseDelimitedString(delimitedString: string) {
|
||||
if (delimitedString == null) {
|
||||
throw new Error("delimitedString is null or undefined");
|
||||
}
|
||||
|
||||
public static timeSpanFromMetrics(metrics: { [key: string]: any } /* TODO: any */, key: string) {
|
||||
if (key in metrics) {
|
||||
return TimeSpan.fromMilliseconds(metrics[key]);
|
||||
const metrics: { [key: string]: any } = {};
|
||||
|
||||
const headerAttributes = delimitedString.split(";");
|
||||
for (const attribute of headerAttributes) {
|
||||
const attributeKeyValue = attribute.split("=");
|
||||
|
||||
if (attributeKeyValue.length !== 2) {
|
||||
throw new Error("recieved a malformed delimited string");
|
||||
}
|
||||
|
||||
return TimeSpan.zero;
|
||||
const attributeKey = attributeKeyValue[0];
|
||||
const attributeValue = parseFloat(attributeKeyValue[1]);
|
||||
|
||||
metrics[attributeKey] = attributeValue;
|
||||
}
|
||||
|
||||
public static isNumeric(input: any) {
|
||||
return !isNaN(parseFloat(input)) && isFinite(input);
|
||||
}
|
||||
return metrics;
|
||||
}
|
||||
|
||||
export function timeSpanFromMetrics(metrics: { [key: string]: any } /* TODO: any */, key: string) {
|
||||
if (key in metrics) {
|
||||
return TimeSpan.fromMilliseconds(metrics[key]);
|
||||
}
|
||||
|
||||
return TimeSpan.zero;
|
||||
}
|
||||
|
||||
export function isNumeric(input: any) {
|
||||
return !isNaN(parseFloat(input)) && isFinite(input);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import QueryMetricsConstants from "./queryMetricsConstants";
|
||||
import { QueryMetricsUtils } from "./queryMetricsUtils";
|
||||
import { parseDelimitedString, timeSpanFromMetrics } from "./queryMetricsUtils";
|
||||
import { TimeSpan } from "./timeSpan";
|
||||
|
||||
export class QueryPreparationTimes {
|
||||
|
@ -76,13 +76,13 @@ export class QueryPreparationTimes {
|
|||
* @instance
|
||||
*/
|
||||
public static createFromDelimitedString(delimitedString: string) {
|
||||
const metrics = QueryMetricsUtils.parseDelimitedString(delimitedString);
|
||||
const metrics = parseDelimitedString(delimitedString);
|
||||
|
||||
return new QueryPreparationTimes(
|
||||
QueryMetricsUtils.timeSpanFromMetrics(metrics, QueryMetricsConstants.QueryCompileTimeInMs),
|
||||
QueryMetricsUtils.timeSpanFromMetrics(metrics, QueryMetricsConstants.LogicalPlanBuildTimeInMs),
|
||||
QueryMetricsUtils.timeSpanFromMetrics(metrics, QueryMetricsConstants.PhysicalPlanBuildTimeInMs),
|
||||
QueryMetricsUtils.timeSpanFromMetrics(metrics, QueryMetricsConstants.QueryOptimizationTimeInMs)
|
||||
timeSpanFromMetrics(metrics, QueryMetricsConstants.QueryCompileTimeInMs),
|
||||
timeSpanFromMetrics(metrics, QueryMetricsConstants.LogicalPlanBuildTimeInMs),
|
||||
timeSpanFromMetrics(metrics, QueryMetricsConstants.PhysicalPlanBuildTimeInMs),
|
||||
timeSpanFromMetrics(metrics, QueryMetricsConstants.QueryOptimizationTimeInMs)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import QueryMetricsConstants from "./queryMetricsConstants";
|
||||
import { QueryMetricsUtils } from "./queryMetricsUtils";
|
||||
import { parseDelimitedString, timeSpanFromMetrics } from "./queryMetricsUtils";
|
||||
import { TimeSpan } from "./timeSpan";
|
||||
|
||||
export class RuntimeExecutionTimes {
|
||||
|
@ -74,15 +74,12 @@ export class RuntimeExecutionTimes {
|
|||
* Returns a new instance of the RuntimeExecutionTimes class this is deserialized from a delimited string.
|
||||
*/
|
||||
public static createFromDelimitedString(delimitedString: string) {
|
||||
const metrics = QueryMetricsUtils.parseDelimitedString(delimitedString);
|
||||
const metrics = parseDelimitedString(delimitedString);
|
||||
|
||||
const vmExecutionTime = QueryMetricsUtils.timeSpanFromMetrics(metrics, QueryMetricsConstants.VMExecutionTimeInMs);
|
||||
const indexLookupTime = QueryMetricsUtils.timeSpanFromMetrics(metrics, QueryMetricsConstants.IndexLookupTimeInMs);
|
||||
const documentLoadTime = QueryMetricsUtils.timeSpanFromMetrics(metrics, QueryMetricsConstants.DocumentLoadTimeInMs);
|
||||
const documentWriteTime = QueryMetricsUtils.timeSpanFromMetrics(
|
||||
metrics,
|
||||
QueryMetricsConstants.DocumentWriteTimeInMs
|
||||
);
|
||||
const vmExecutionTime = timeSpanFromMetrics(metrics, QueryMetricsConstants.VMExecutionTimeInMs);
|
||||
const indexLookupTime = timeSpanFromMetrics(metrics, QueryMetricsConstants.IndexLookupTimeInMs);
|
||||
const documentLoadTime = timeSpanFromMetrics(metrics, QueryMetricsConstants.DocumentLoadTimeInMs);
|
||||
const documentWriteTime = timeSpanFromMetrics(metrics, QueryMetricsConstants.DocumentWriteTimeInMs);
|
||||
|
||||
let queryEngineExecutionTime = TimeSpan.zero;
|
||||
queryEngineExecutionTime = queryEngineExecutionTime.add(vmExecutionTime);
|
||||
|
@ -91,8 +88,8 @@ export class RuntimeExecutionTimes {
|
|||
queryEngineExecutionTime = queryEngineExecutionTime.subtract(documentWriteTime);
|
||||
return new RuntimeExecutionTimes(
|
||||
queryEngineExecutionTime,
|
||||
QueryMetricsUtils.timeSpanFromMetrics(metrics, QueryMetricsConstants.SystemFunctionExecuteTimeInMs),
|
||||
QueryMetricsUtils.timeSpanFromMetrics(metrics, QueryMetricsConstants.UserDefinedFunctionExecutionTimeInMs)
|
||||
timeSpanFromMetrics(metrics, QueryMetricsConstants.SystemFunctionExecuteTimeInMs),
|
||||
timeSpanFromMetrics(metrics, QueryMetricsConstants.UserDefinedFunctionExecutionTimeInMs)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as querystring from "querystring";
|
|||
import { ConnectionPolicy } from "../documents";
|
||||
import { GlobalEndpointManager } from "../globalEndpointManager";
|
||||
import { Constants, IHeaders } from "../index";
|
||||
import { RetryUtility } from "../retry";
|
||||
import * as RetryUtility from "../retry/retryUtility";
|
||||
import { bodyFromData, createRequestObject, parse, Response } from "./request";
|
||||
import { RequestContext } from "./RequestContext";
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@ import { Socket } from "net";
|
|||
import { Stream } from "stream";
|
||||
import * as url from "url";
|
||||
|
||||
import { Constants, Helper } from "../common";
|
||||
import { Constants, jsonStringifyAndEscapeNonASCII } from "../common";
|
||||
import { ConnectionPolicy, MediaReadMode } from "../documents";
|
||||
import { IHeaders } from "../queryExecutionContext";
|
||||
|
||||
import { ErrorResponse } from "./ErrorResponse";
|
||||
export { ErrorResponse }; // Should refactor this out
|
||||
|
||||
import { AuthHandler, AuthOptions } from "../auth";
|
||||
import { AuthOptions, getAuthorizationHeader } from "../auth";
|
||||
import { FeedOptions, MediaOptions, RequestOptions } from "./index";
|
||||
import { Response } from "./Response";
|
||||
export { Response }; // Should refactor this out
|
||||
|
@ -261,7 +261,7 @@ export async function getHeaders(
|
|||
if (partitionKey === null || !Array.isArray(partitionKey)) {
|
||||
partitionKey = [partitionKey as string];
|
||||
}
|
||||
headers[Constants.HttpHeaders.PartitionKey] = Helper.jsonStringifyAndEscapeNonASCII(partitionKey);
|
||||
headers[Constants.HttpHeaders.PartitionKey] = jsonStringifyAndEscapeNonASCII(partitionKey);
|
||||
}
|
||||
|
||||
if (authOptions.masterKey || authOptions.key || authOptions.tokenProvider) {
|
||||
|
@ -300,7 +300,7 @@ export async function getHeaders(
|
|||
authOptions.tokenProvider ||
|
||||
authOptions.permissionFeed
|
||||
) {
|
||||
const token = await AuthHandler.getAuthorizationHeader(authOptions, verb, path, resourceId, resourceType, headers);
|
||||
const token = await getAuthorizationHeader(authOptions, verb, path, resourceId, resourceType, headers);
|
||||
headers[Constants.HttpHeaders.Authorization] = token;
|
||||
}
|
||||
return headers;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Helper } from "../common";
|
||||
import { isReadRequest } from "../common";
|
||||
import { GlobalEndpointManager } from "../globalEndpointManager";
|
||||
import { ErrorResponse } from "../request/request";
|
||||
import { RequestContext } from "../request/RequestContext";
|
||||
|
@ -57,7 +57,7 @@ export class EndpointDiscoveryRetryPolicy implements IRetryPolicy {
|
|||
|
||||
this.currentRetryAttemptCount++;
|
||||
|
||||
if (Helper.isReadRequest(this.request)) {
|
||||
if (isReadRequest(this.request)) {
|
||||
this.globalEndpointManager.markCurrentLocationUnavailableForRead(locationEndpoint);
|
||||
} else {
|
||||
this.globalEndpointManager.markCurrentLocationUnavailableForWrite(locationEndpoint);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { RequestOptions } from "https";
|
||||
import * as url from "url";
|
||||
import { Constants, Helper, StatusCodes, SubStatusCodes } from "../common";
|
||||
import { Constants, sleep, StatusCodes, SubStatusCodes } from "../common";
|
||||
import { ConnectionPolicy } from "../documents";
|
||||
import { GlobalEndpointManager } from "../globalEndpointManager";
|
||||
import { Response } from "../request";
|
||||
|
@ -18,150 +18,147 @@ export type CreateRequestObjectStubFunction = (
|
|||
body: Buffer
|
||||
) => Promise<Response<any>>; // TODO: any response
|
||||
|
||||
/** @hidden */
|
||||
export class RetryUtility {
|
||||
/**
|
||||
* Executes the retry policy for the created request object.
|
||||
* @param {object} globalEndpointManager - an instance of GlobalEndpointManager class.
|
||||
* @param {object} body - request body. A buffer or a string.
|
||||
* @param {function} createRequestObjectStub - stub function that creates the request object.
|
||||
* @param {object} connectionPolicy - an instance of ConnectionPolicy that has the connection configs.
|
||||
* @param {RequestOptions} requestOptions - The request options.
|
||||
* @param {function} callback - the callback that will be called when the request is finished executing.
|
||||
*/
|
||||
public static async execute(
|
||||
globalEndpointManager: GlobalEndpointManager,
|
||||
body: Buffer,
|
||||
createRequestObjectFunc: CreateRequestObjectStubFunction,
|
||||
connectionPolicy: ConnectionPolicy,
|
||||
requestOptions: RequestOptions,
|
||||
request: RequestContext
|
||||
): Promise<Response<any>> {
|
||||
// TODO: any request
|
||||
const r: RequestContext = typeof request !== "string" ? request : { path: "", operationType: "nonReadOps" };
|
||||
/**
|
||||
* Executes the retry policy for the created request object.
|
||||
* @param {object} globalEndpointManager - an instance of GlobalEndpointManager class.
|
||||
* @param {object} body - request body. A buffer or a string.
|
||||
* @param {function} createRequestObjectStub - stub function that creates the request object.
|
||||
* @param {object} connectionPolicy - an instance of ConnectionPolicy that has the connection configs.
|
||||
* @param {RequestOptions} requestOptions - The request options.
|
||||
* @param {function} callback - the callback that will be called when the request is finished executing.
|
||||
*/
|
||||
export async function execute(
|
||||
globalEndpointManager: GlobalEndpointManager,
|
||||
body: Buffer,
|
||||
createRequestObjectFunc: CreateRequestObjectStubFunction,
|
||||
connectionPolicy: ConnectionPolicy,
|
||||
requestOptions: RequestOptions,
|
||||
request: RequestContext
|
||||
): Promise<Response<any>> {
|
||||
// TODO: any request
|
||||
const r: RequestContext = typeof request !== "string" ? request : { path: "", operationType: "nonReadOps" };
|
||||
|
||||
const endpointDiscoveryRetryPolicy = new EndpointDiscoveryRetryPolicy(globalEndpointManager, r);
|
||||
const resourceThrottleRetryPolicy = new ResourceThrottleRetryPolicy(
|
||||
connectionPolicy.RetryOptions.MaxRetryAttemptCount,
|
||||
connectionPolicy.RetryOptions.FixedRetryIntervalInMilliseconds,
|
||||
connectionPolicy.RetryOptions.MaxWaitTimeInSeconds
|
||||
);
|
||||
const sessionReadRetryPolicy = new SessionRetryPolicy(globalEndpointManager, r, connectionPolicy);
|
||||
const defaultRetryPolicy = new DefaultRetryPolicy(request.operationType);
|
||||
const endpointDiscoveryRetryPolicy = new EndpointDiscoveryRetryPolicy(globalEndpointManager, r);
|
||||
const resourceThrottleRetryPolicy = new ResourceThrottleRetryPolicy(
|
||||
connectionPolicy.RetryOptions.MaxRetryAttemptCount,
|
||||
connectionPolicy.RetryOptions.FixedRetryIntervalInMilliseconds,
|
||||
connectionPolicy.RetryOptions.MaxWaitTimeInSeconds
|
||||
);
|
||||
const sessionReadRetryPolicy = new SessionRetryPolicy(globalEndpointManager, r, connectionPolicy);
|
||||
const defaultRetryPolicy = new DefaultRetryPolicy(request.operationType);
|
||||
|
||||
return this.apply(
|
||||
body,
|
||||
createRequestObjectFunc,
|
||||
connectionPolicy,
|
||||
requestOptions,
|
||||
endpointDiscoveryRetryPolicy,
|
||||
resourceThrottleRetryPolicy,
|
||||
sessionReadRetryPolicy,
|
||||
defaultRetryPolicy,
|
||||
globalEndpointManager,
|
||||
request,
|
||||
{}
|
||||
);
|
||||
return apply(
|
||||
body,
|
||||
createRequestObjectFunc,
|
||||
connectionPolicy,
|
||||
requestOptions,
|
||||
endpointDiscoveryRetryPolicy,
|
||||
resourceThrottleRetryPolicy,
|
||||
sessionReadRetryPolicy,
|
||||
defaultRetryPolicy,
|
||||
globalEndpointManager,
|
||||
request,
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the retry policy for the created request object.
|
||||
* @param {object} body - request body. A buffer or a string.
|
||||
* @param {function} createRequestObjectFunc - function that creates the request object.
|
||||
* @param {object} connectionPolicy - an instance of ConnectionPolicy that has the connection configs.
|
||||
* @param {RequestOptions} requestOptions - The request options.
|
||||
* @param {EndpointDiscoveryRetryPolicy} endpointDiscoveryRetryPolicy - The endpoint discovery retry policy \
|
||||
* instance.
|
||||
* @param {ResourceThrottleRetryPolicy} resourceThrottleRetryPolicy - The resource throttle retry policy instance.
|
||||
* @param {function} callback - the callback that will be called when the response is retrieved and processed.
|
||||
*/
|
||||
export async function apply(
|
||||
body: Buffer,
|
||||
createRequestObjectFunc: CreateRequestObjectStubFunction,
|
||||
connectionPolicy: ConnectionPolicy,
|
||||
requestOptions: RequestOptions,
|
||||
endpointDiscoveryRetryPolicy: EndpointDiscoveryRetryPolicy,
|
||||
resourceThrottleRetryPolicy: ResourceThrottleRetryPolicy,
|
||||
sessionReadRetryPolicy: SessionRetryPolicy,
|
||||
defaultRetryPolicy: DefaultRetryPolicy,
|
||||
globalEndpointManager: GlobalEndpointManager,
|
||||
request: RequestContext,
|
||||
retryContext: RetryContext
|
||||
): Promise<Response<any>> {
|
||||
// TODO: any response
|
||||
const httpsRequest = createRequestObjectFunc(connectionPolicy, requestOptions, body);
|
||||
if (!request.locationRouting) {
|
||||
request.locationRouting = new LocationRouting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the retry policy for the created request object.
|
||||
* @param {object} body - request body. A buffer or a string.
|
||||
* @param {function} createRequestObjectFunc - function that creates the request object.
|
||||
* @param {object} connectionPolicy - an instance of ConnectionPolicy that has the connection configs.
|
||||
* @param {RequestOptions} requestOptions - The request options.
|
||||
* @param {EndpointDiscoveryRetryPolicy} endpointDiscoveryRetryPolicy - The endpoint discovery retry policy \
|
||||
* instance.
|
||||
* @param {ResourceThrottleRetryPolicy} resourceThrottleRetryPolicy - The resource throttle retry policy instance.
|
||||
* @param {function} callback - the callback that will be called when the response is retrieved and processed.
|
||||
*/
|
||||
public static async apply(
|
||||
body: Buffer,
|
||||
createRequestObjectFunc: CreateRequestObjectStubFunction,
|
||||
connectionPolicy: ConnectionPolicy,
|
||||
requestOptions: RequestOptions,
|
||||
endpointDiscoveryRetryPolicy: EndpointDiscoveryRetryPolicy,
|
||||
resourceThrottleRetryPolicy: ResourceThrottleRetryPolicy,
|
||||
sessionReadRetryPolicy: SessionRetryPolicy,
|
||||
defaultRetryPolicy: DefaultRetryPolicy,
|
||||
globalEndpointManager: GlobalEndpointManager,
|
||||
request: RequestContext,
|
||||
retryContext: RetryContext
|
||||
): Promise<Response<any>> {
|
||||
// TODO: any response
|
||||
const httpsRequest = createRequestObjectFunc(connectionPolicy, requestOptions, body);
|
||||
if (!request.locationRouting) {
|
||||
request.locationRouting = new LocationRouting();
|
||||
request.locationRouting.clearRouteToLocation();
|
||||
if (retryContext) {
|
||||
request.locationRouting.routeToLocation(
|
||||
retryContext.retryCount || 0,
|
||||
!retryContext.retryRequestOnPreferredLocations
|
||||
);
|
||||
if (retryContext.clearSessionTokenNotAvailable) {
|
||||
request.client.clearSessionToken(request.path);
|
||||
}
|
||||
request.locationRouting.clearRouteToLocation();
|
||||
if (retryContext) {
|
||||
request.locationRouting.routeToLocation(
|
||||
retryContext.retryCount || 0,
|
||||
!retryContext.retryRequestOnPreferredLocations
|
||||
);
|
||||
if (retryContext.clearSessionTokenNotAvailable) {
|
||||
request.client.clearSessionToken(request.path);
|
||||
}
|
||||
}
|
||||
const locationEndpoint = await globalEndpointManager.resolveServiceEndpoint(request);
|
||||
requestOptions = modifyRequestOptions(requestOptions, url.parse(locationEndpoint));
|
||||
request.locationRouting.routeToLocation(locationEndpoint);
|
||||
try {
|
||||
const { result, headers } = await (httpsRequest as Promise<Response<any>>);
|
||||
headers[Constants.ThrottleRetryCount] = resourceThrottleRetryPolicy.currentRetryAttemptCount;
|
||||
headers[Constants.ThrottleRetryWaitTimeInMs] = resourceThrottleRetryPolicy.cummulativeWaitTimeinMilliseconds;
|
||||
return { result, headers };
|
||||
} catch (err) {
|
||||
// TODO: any error
|
||||
let retryPolicy: IRetryPolicy = null;
|
||||
const headers = err.headers || {};
|
||||
if (err.code === StatusCodes.Forbidden && err.substatus === SubStatusCodes.WriteForbidden) {
|
||||
retryPolicy = endpointDiscoveryRetryPolicy;
|
||||
} else if (err.code === StatusCodes.TooManyRequests) {
|
||||
retryPolicy = resourceThrottleRetryPolicy;
|
||||
} else if (err.code === StatusCodes.NotFound && err.substatus === SubStatusCodes.ReadSessionNotAvailable) {
|
||||
retryPolicy = sessionReadRetryPolicy;
|
||||
} else {
|
||||
retryPolicy = defaultRetryPolicy;
|
||||
}
|
||||
const locationEndpoint = await globalEndpointManager.resolveServiceEndpoint(request);
|
||||
requestOptions = this.modifyRequestOptions(requestOptions, url.parse(locationEndpoint));
|
||||
request.locationRouting.routeToLocation(locationEndpoint);
|
||||
try {
|
||||
const { result, headers } = await (httpsRequest as Promise<Response<any>>);
|
||||
const results = await retryPolicy.shouldRetry(err, retryContext);
|
||||
if (!results) {
|
||||
headers[Constants.ThrottleRetryCount] = resourceThrottleRetryPolicy.currentRetryAttemptCount;
|
||||
headers[Constants.ThrottleRetryWaitTimeInMs] = resourceThrottleRetryPolicy.cummulativeWaitTimeinMilliseconds;
|
||||
return { result, headers };
|
||||
} catch (err) {
|
||||
// TODO: any error
|
||||
let retryPolicy: IRetryPolicy = null;
|
||||
const headers = err.headers || {};
|
||||
if (err.code === StatusCodes.Forbidden && err.substatus === SubStatusCodes.WriteForbidden) {
|
||||
retryPolicy = endpointDiscoveryRetryPolicy;
|
||||
} else if (err.code === StatusCodes.TooManyRequests) {
|
||||
retryPolicy = resourceThrottleRetryPolicy;
|
||||
} else if (err.code === StatusCodes.NotFound && err.substatus === SubStatusCodes.ReadSessionNotAvailable) {
|
||||
retryPolicy = sessionReadRetryPolicy;
|
||||
} else {
|
||||
retryPolicy = defaultRetryPolicy;
|
||||
}
|
||||
const results = await retryPolicy.shouldRetry(err, retryContext);
|
||||
if (!results) {
|
||||
headers[Constants.ThrottleRetryCount] = resourceThrottleRetryPolicy.currentRetryAttemptCount;
|
||||
headers[Constants.ThrottleRetryWaitTimeInMs] = resourceThrottleRetryPolicy.cummulativeWaitTimeinMilliseconds;
|
||||
err.headers = { ...err.headers, ...headers };
|
||||
throw err;
|
||||
} else {
|
||||
request.retryCount++;
|
||||
const newUrl = (results as any)[1]; // TODO: any hack
|
||||
await Helper.sleep(retryPolicy.retryAfterInMilliseconds);
|
||||
return this.apply(
|
||||
body,
|
||||
createRequestObjectFunc,
|
||||
connectionPolicy,
|
||||
requestOptions,
|
||||
endpointDiscoveryRetryPolicy,
|
||||
resourceThrottleRetryPolicy,
|
||||
sessionReadRetryPolicy,
|
||||
defaultRetryPolicy,
|
||||
globalEndpointManager,
|
||||
request,
|
||||
retryContext
|
||||
);
|
||||
}
|
||||
err.headers = { ...err.headers, ...headers };
|
||||
throw err;
|
||||
} else {
|
||||
request.retryCount++;
|
||||
const newUrl = (results as any)[1]; // TODO: any hack
|
||||
await sleep(retryPolicy.retryAfterInMilliseconds);
|
||||
return apply(
|
||||
body,
|
||||
createRequestObjectFunc,
|
||||
connectionPolicy,
|
||||
requestOptions,
|
||||
endpointDiscoveryRetryPolicy,
|
||||
resourceThrottleRetryPolicy,
|
||||
sessionReadRetryPolicy,
|
||||
defaultRetryPolicy,
|
||||
globalEndpointManager,
|
||||
request,
|
||||
retryContext
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static 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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Helper } from "../common";
|
||||
import { isReadRequest } from "../common";
|
||||
import { ConnectionPolicy } from "../documents";
|
||||
import { GlobalEndpointManager } from "../globalEndpointManager";
|
||||
import { ErrorResponse } from "../request/request";
|
||||
|
@ -48,7 +48,7 @@ export class SessionRetryPolicy implements IRetryPolicy {
|
|||
|
||||
if (this.globalEndpointManager.canUseMultipleWriteLocations(this.request)) {
|
||||
// If we can write to multiple locations, we should against every write endpoint until we succeed
|
||||
const endpoints = Helper.isReadRequest(this.request)
|
||||
const endpoints = isReadRequest(this.request)
|
||||
? await this.globalEndpointManager.getReadEndpoints()
|
||||
: await this.globalEndpointManager.getWriteEndpoints();
|
||||
if (this.currentRetryAttemptCount > endpoints.length) {
|
||||
|
|
|
@ -14,69 +14,67 @@ function compareRanges(a: any, b: any) {
|
|||
}
|
||||
|
||||
/** @hidden */
|
||||
export class CollectionRoutingMapFactory {
|
||||
public static createCompleteRoutingMap(partitionKeyRangeInfoTuppleList: any[], collectionUniqueId: string) {
|
||||
const rangeById: any = {}; // TODO: any
|
||||
const rangeByInfo: any = {}; // TODO: any
|
||||
export function createCompleteRoutingMap(partitionKeyRangeInfoTuppleList: any[], collectionUniqueId: string) {
|
||||
const rangeById: any = {}; // TODO: any
|
||||
const rangeByInfo: any = {}; // TODO: any
|
||||
|
||||
let sortedRanges = [];
|
||||
let sortedRanges = [];
|
||||
|
||||
// the for loop doesn't invoke any async callback
|
||||
for (const r of partitionKeyRangeInfoTuppleList) {
|
||||
rangeById[r[0][Constants.PartitionKeyRange.Id]] = r;
|
||||
rangeByInfo[r[1]] = r[0];
|
||||
sortedRanges.push(r);
|
||||
}
|
||||
|
||||
sortedRanges = sortedRanges.sort(compareRanges);
|
||||
const partitionKeyOrderedRange = sortedRanges.map(r => r[0]);
|
||||
const orderedPartitionInfo = sortedRanges.map(r => r[1]);
|
||||
|
||||
if (!this._isCompleteSetOfRange(partitionKeyOrderedRange)) {
|
||||
return undefined;
|
||||
}
|
||||
return new InMemoryCollectionRoutingMap(
|
||||
rangeById,
|
||||
rangeByInfo,
|
||||
partitionKeyOrderedRange,
|
||||
orderedPartitionInfo,
|
||||
collectionUniqueId
|
||||
);
|
||||
// the for loop doesn't invoke any async callback
|
||||
for (const r of partitionKeyRangeInfoTuppleList) {
|
||||
rangeById[r[0][Constants.PartitionKeyRange.Id]] = r;
|
||||
rangeByInfo[r[1]] = r[0];
|
||||
sortedRanges.push(r);
|
||||
}
|
||||
|
||||
private static _isCompleteSetOfRange(partitionKeyOrderedRange: any) {
|
||||
// TODO: any
|
||||
let isComplete = false;
|
||||
if (partitionKeyOrderedRange.length > 0) {
|
||||
const firstRange = partitionKeyOrderedRange[0];
|
||||
const lastRange = partitionKeyOrderedRange[partitionKeyOrderedRange.length - 1];
|
||||
isComplete =
|
||||
firstRange[Constants.PartitionKeyRange.MinInclusive] ===
|
||||
Constants.EffectiveParitionKeyConstants.MinimumInclusiveEffectivePartitionKey;
|
||||
sortedRanges = sortedRanges.sort(compareRanges);
|
||||
const partitionKeyOrderedRange = sortedRanges.map(r => r[0]);
|
||||
const orderedPartitionInfo = sortedRanges.map(r => r[1]);
|
||||
|
||||
if (!isCompleteSetOfRange(partitionKeyOrderedRange)) {
|
||||
return undefined;
|
||||
}
|
||||
return new InMemoryCollectionRoutingMap(
|
||||
rangeById,
|
||||
rangeByInfo,
|
||||
partitionKeyOrderedRange,
|
||||
orderedPartitionInfo,
|
||||
collectionUniqueId
|
||||
);
|
||||
}
|
||||
|
||||
function isCompleteSetOfRange(partitionKeyOrderedRange: any) {
|
||||
// TODO: any
|
||||
let isComplete = false;
|
||||
if (partitionKeyOrderedRange.length > 0) {
|
||||
const firstRange = partitionKeyOrderedRange[0];
|
||||
const lastRange = partitionKeyOrderedRange[partitionKeyOrderedRange.length - 1];
|
||||
isComplete =
|
||||
firstRange[Constants.PartitionKeyRange.MinInclusive] ===
|
||||
Constants.EffectiveParitionKeyConstants.MinimumInclusiveEffectivePartitionKey;
|
||||
isComplete =
|
||||
isComplete &&
|
||||
lastRange[Constants.PartitionKeyRange.MaxExclusive] ===
|
||||
Constants.EffectiveParitionKeyConstants.MaximumExclusiveEffectivePartitionKey;
|
||||
|
||||
for (let i = 1; i < partitionKeyOrderedRange.length; i++) {
|
||||
const previousRange = partitionKeyOrderedRange[i - 1];
|
||||
const currentRange = partitionKeyOrderedRange[i];
|
||||
isComplete =
|
||||
isComplete &&
|
||||
lastRange[Constants.PartitionKeyRange.MaxExclusive] ===
|
||||
Constants.EffectiveParitionKeyConstants.MaximumExclusiveEffectivePartitionKey;
|
||||
previousRange[Constants.PartitionKeyRange.MaxExclusive] ===
|
||||
currentRange[Constants.PartitionKeyRange.MinInclusive];
|
||||
|
||||
for (let i = 1; i < partitionKeyOrderedRange.length; i++) {
|
||||
const previousRange = partitionKeyOrderedRange[i - 1];
|
||||
const currentRange = partitionKeyOrderedRange[i];
|
||||
isComplete =
|
||||
isComplete &&
|
||||
previousRange[Constants.PartitionKeyRange.MaxExclusive] ===
|
||||
currentRange[Constants.PartitionKeyRange.MinInclusive];
|
||||
|
||||
if (!isComplete) {
|
||||
if (
|
||||
previousRange[Constants.PartitionKeyRange.MaxExclusive] >
|
||||
currentRange[Constants.PartitionKeyRange.MinInclusive]
|
||||
) {
|
||||
throw Error("Ranges overlap");
|
||||
}
|
||||
break;
|
||||
if (!isComplete) {
|
||||
if (
|
||||
previousRange[Constants.PartitionKeyRange.MaxExclusive] >
|
||||
currentRange[Constants.PartitionKeyRange.MinInclusive]
|
||||
) {
|
||||
throw Error("Ranges overlap");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isComplete;
|
||||
}
|
||||
return isComplete;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
export * from "./QueryRange";
|
||||
export * from "./CollectionRoutingMapFactory";
|
||||
export * from "./inMemoryCollectionRoutingMap";
|
||||
export * from "./partitionKeyRangeCache";
|
||||
export * from "./smartRoutingMapProvider";
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import semaphore from "semaphore";
|
||||
import { ClientContext } from "../ClientContext";
|
||||
import { Helper } from "../common";
|
||||
import { CollectionRoutingMapFactory, InMemoryCollectionRoutingMap, QueryRange } from "./index";
|
||||
import { getIdFromLink } from "../common";
|
||||
import { createCompleteRoutingMap } from "./CollectionRoutingMapFactory";
|
||||
import { InMemoryCollectionRoutingMap, QueryRange } from "./index";
|
||||
|
||||
/** @hidden */
|
||||
export class PartitionKeyRangeCache {
|
||||
|
@ -22,7 +23,7 @@ export class PartitionKeyRangeCache {
|
|||
* @ignore
|
||||
*/
|
||||
public async onCollectionRoutingMap(collectionLink: string): Promise<InMemoryCollectionRoutingMap> {
|
||||
const collectionId = Helper.getIdFromLink(collectionLink);
|
||||
const collectionId = getIdFromLink(collectionLink);
|
||||
|
||||
let collectionRoutingMap = this.collectionRoutingMapByCollectionId[collectionId];
|
||||
if (collectionRoutingMap === undefined) {
|
||||
|
@ -34,7 +35,7 @@ export class PartitionKeyRangeCache {
|
|||
try {
|
||||
const { result: resources } = await this.clientContext.queryPartitionKeyRanges(collectionLink).toArray();
|
||||
|
||||
crm = CollectionRoutingMapFactory.createCompleteRoutingMap(resources.map(r => [r, true]), collectionId);
|
||||
crm = createCompleteRoutingMap(resources.map(r => [r, true]), collectionId);
|
||||
|
||||
this.collectionRoutingMapByCollectionId[collectionId] = crm;
|
||||
this.sem.leave();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Constants, Helper } from "../common";
|
||||
import { Constants, getContainerLink, trimSlashes } from "../common";
|
||||
import { IHeaders } from "../queryExecutionContext";
|
||||
import { SessionContext } from "./SessionContext";
|
||||
import { VectorSessionToken } from "./VectorSessionToken";
|
||||
|
@ -17,15 +17,15 @@ export class SessionContainer {
|
|||
if (!request) {
|
||||
throw new Error("request cannot be null");
|
||||
}
|
||||
const collectionName = Helper.getContainerLink(Helper.trimSlashes(request.resourceAddress));
|
||||
const collectionName = getContainerLink(trimSlashes(request.resourceAddress));
|
||||
const rangeIdToTokenMap = this.getPartitionKeyRangeIdToTokenMap(collectionName);
|
||||
return SessionContainer.getCombinedSessionTokenString(rangeIdToTokenMap);
|
||||
}
|
||||
|
||||
public remove(request: SessionContext) {
|
||||
let collectionResourceId: string;
|
||||
const resourceAddress = Helper.trimSlashes(request.resourceAddress);
|
||||
const collectionName = Helper.getContainerLink(resourceAddress);
|
||||
const resourceAddress = trimSlashes(request.resourceAddress);
|
||||
const collectionName = getContainerLink(resourceAddress);
|
||||
if (collectionName) {
|
||||
collectionResourceId = this.collectionNameToCollectionResourceId.get(collectionName);
|
||||
this.collectionNameToCollectionResourceId.delete(collectionName);
|
||||
|
@ -149,9 +149,9 @@ export class SessionContainer {
|
|||
private getContainerName(request: SessionContext, headers: IHeaders) {
|
||||
let ownerFullName = headers[Constants.HttpHeaders.OwnerFullName];
|
||||
if (!ownerFullName) {
|
||||
ownerFullName = Helper.trimSlashes(request.resourceAddress);
|
||||
ownerFullName = trimSlashes(request.resourceAddress);
|
||||
}
|
||||
|
||||
return Helper.getContainerLink(ownerFullName as string);
|
||||
return getContainerLink(ownerFullName as string);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,9 @@
|
|||
import assert from "assert";
|
||||
import { RequestOptions } from "../..";
|
||||
import { Container, ContainerDefinition } from "../../client";
|
||||
import { Helper } from "../../common";
|
||||
import { sleep } from "../../common";
|
||||
import { getTestContainer, removeAllDatabases } from "../common/TestHelpers";
|
||||
|
||||
function hasDupeKey(items: any[]) {
|
||||
if (items && items.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const key = items[0].key;
|
||||
let hasDupe = false;
|
||||
for (const item of items) {
|
||||
if (item.key !== key) {
|
||||
hasDupe = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return hasDupe;
|
||||
}
|
||||
|
||||
describe("Change Feed Iterator", function() {
|
||||
this.timeout(process.env.MOCHA_TIMEOUT || 20000);
|
||||
|
||||
|
@ -43,7 +28,7 @@ describe("Change Feed Iterator", function() {
|
|||
it("should fetch updated items only with start time", async function() {
|
||||
await container.items.create({ id: "item1" });
|
||||
const date = new Date();
|
||||
await Helper.sleep(3000);
|
||||
await sleep(3000);
|
||||
await container.items.create({ id: "item2" });
|
||||
const iterator = container.items.readChangeFeed({ startTime: date });
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import assert from "assert";
|
||||
import * as sinon from "sinon";
|
||||
import { ClientContext } from "../../ClientContext";
|
||||
import { Helper } from "../../common";
|
||||
import { trimSlashes } from "../../common";
|
||||
import { ConsistencyLevel, PartitionKind } from "../../documents";
|
||||
import { Constants, CosmosClient, IHeaders } from "../../index";
|
||||
import { RequestHandler } from "../../request";
|
||||
|
@ -296,7 +296,7 @@ describe("Session Token", function() {
|
|||
} catch (err) {
|
||||
assert.equal(err.substatus, 1002, "Substatus should indicate the LSN didn't catchup.");
|
||||
assert.equal(callbackSpy.callCount, 1);
|
||||
assert.equal(Helper.trimSlashes(callbackSpy.lastCall.args[0]), containerLink + "/docs/1");
|
||||
assert.equal(trimSlashes(callbackSpy.lastCall.args[0]), containerLink + "/docs/1");
|
||||
} finally {
|
||||
applySessionTokenStub.restore();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"space-before-function-paren": false,
|
||||
"no-console": false,
|
||||
"no-implicit-dependencies": [true, "dev"],
|
||||
"import-blacklist": false
|
||||
"import-blacklist": false,
|
||||
"no-invalid-this": false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import assert from "assert";
|
||||
import { IHeaders } from "../..";
|
||||
import { Constants, Helper } from "../../common";
|
||||
import { Constants, isResourceValid, setIsUpsertHeader } from "../../common";
|
||||
|
||||
describe("Helper methods", function() {
|
||||
describe("isResourceValid Unit Tests", function() {
|
||||
it("id is not string", function(done) {
|
||||
const err = {};
|
||||
const result = Helper.isResourceValid({ id: 1 }, err);
|
||||
const result = isResourceValid({ id: 1 }, err);
|
||||
|
||||
assert.equal(result, false);
|
||||
assert.deepEqual(err, { message: "Id must be a string." });
|
||||
|
@ -18,7 +18,7 @@ describe("Helper methods", function() {
|
|||
it("Should add is-upsert header.", function(done) {
|
||||
const headers: any = {};
|
||||
assert.equal(undefined, headers[Constants.HttpHeaders.IsUpsert]);
|
||||
Helper.setIsUpsertHeader(headers);
|
||||
setIsUpsertHeader(headers);
|
||||
assert.equal(true, headers[Constants.HttpHeaders.IsUpsert]);
|
||||
done();
|
||||
});
|
||||
|
@ -27,21 +27,21 @@ describe("Helper methods", function() {
|
|||
const headers: IHeaders = {};
|
||||
headers[Constants.HttpHeaders.IsUpsert] = false;
|
||||
assert.equal(false, headers[Constants.HttpHeaders.IsUpsert]);
|
||||
Helper.setIsUpsertHeader(headers);
|
||||
setIsUpsertHeader(headers);
|
||||
assert.equal(true, headers[Constants.HttpHeaders.IsUpsert]);
|
||||
done();
|
||||
});
|
||||
|
||||
it("Should throw on undefined headers", function(done) {
|
||||
assert.throws(function() {
|
||||
Helper.setIsUpsertHeader(undefined);
|
||||
setIsUpsertHeader(undefined);
|
||||
}, /The "headers" parameter must not be null or undefined/);
|
||||
done();
|
||||
});
|
||||
|
||||
it("Should throw on null headers", function(done) {
|
||||
assert.throws(function() {
|
||||
Helper.setIsUpsertHeader(null);
|
||||
setIsUpsertHeader(null);
|
||||
}, /The "headers" parameter must not be null or undefined/);
|
||||
done();
|
||||
});
|
||||
|
@ -49,7 +49,7 @@ describe("Helper methods", function() {
|
|||
it("Should throw on invalid string headers", function(done) {
|
||||
assert.throws(
|
||||
function() {
|
||||
Helper.setIsUpsertHeader("" as any);
|
||||
setIsUpsertHeader("" as any);
|
||||
}, // Any type is intentional for test failure
|
||||
/The "headers" parameter must be an instance of "Object". Actual type is: "string"./
|
||||
);
|
||||
|
@ -59,7 +59,7 @@ describe("Helper methods", function() {
|
|||
it("Should throw on invalid number headers", function(done) {
|
||||
assert.throws(
|
||||
function() {
|
||||
Helper.setIsUpsertHeader(0 as any);
|
||||
setIsUpsertHeader(0 as any);
|
||||
}, // Any type is intentional for test failure
|
||||
/The "headers" parameter must be an instance of "Object". Actual type is: "number"./
|
||||
);
|
||||
|
@ -69,7 +69,7 @@ describe("Helper methods", function() {
|
|||
it("Should throw on invalid boolean headers", function(done) {
|
||||
assert.throws(
|
||||
function() {
|
||||
Helper.setIsUpsertHeader(false as any);
|
||||
setIsUpsertHeader(false as any);
|
||||
}, // Any type is intentional for test failure
|
||||
/The "headers" parameter must be an instance of "Object". Actual type is: "boolean"./
|
||||
);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import assert from "assert";
|
||||
import { CollectionRoutingMapFactory, InMemoryCollectionRoutingMap, QueryRange } from "../../routing";
|
||||
import assert from "assert";
|
||||
import { QueryRange } from "../../routing";
|
||||
import { createCompleteRoutingMap } from "../../routing/CollectionRoutingMapFactory";
|
||||
|
||||
describe("InMemoryCollectionRoutingMap Tests", function() {
|
||||
describe("getOverlappingRanges", function() {
|
||||
|
@ -23,10 +24,7 @@ describe("InMemoryCollectionRoutingMap Tests", function() {
|
|||
{ id: "4", minInclusive: "05C1E9CD673398", maxExclusive: "FF" }
|
||||
];
|
||||
const partitionRangeWithInfo = partitionKeyRanges.map(r => [r, true]);
|
||||
const collectionRoutingMap = CollectionRoutingMapFactory.createCompleteRoutingMap(
|
||||
partitionRangeWithInfo,
|
||||
"sample collection id"
|
||||
);
|
||||
const collectionRoutingMap = createCompleteRoutingMap(partitionRangeWithInfo, "sample collection id");
|
||||
|
||||
it("queryCompleteRange", function() {
|
||||
const completeRange = new QueryRange("", "FF", true, false);
|
||||
|
@ -99,10 +97,7 @@ describe("InMemoryCollectionRoutingMap Tests", function() {
|
|||
]
|
||||
];
|
||||
|
||||
const collectionRoutingMap = CollectionRoutingMapFactory.createCompleteRoutingMap(
|
||||
partitionRangeWithInfo,
|
||||
"sample collection id"
|
||||
);
|
||||
const collectionRoutingMap = createCompleteRoutingMap(partitionRangeWithInfo, "sample collection id");
|
||||
|
||||
it("validate _orderedPartitionKeyRanges", function() {
|
||||
assert.equal("0", collectionRoutingMap.getOrderedParitionKeyRanges()[0].id);
|
||||
|
@ -195,10 +190,7 @@ describe("InMemoryCollectionRoutingMap Tests", function() {
|
|||
];
|
||||
const collectionUniqueId = "";
|
||||
try {
|
||||
const collectionRoutingMap = CollectionRoutingMapFactory.createCompleteRoutingMap(
|
||||
partitionRangeWithInfo,
|
||||
"sample collection id"
|
||||
);
|
||||
const collectionRoutingMap = createCompleteRoutingMap(partitionRangeWithInfo, "sample collection id");
|
||||
assert.fail("must throw exception");
|
||||
} catch (e) {
|
||||
assert.equal(e.message, "Ranges overlap");
|
||||
|
@ -225,10 +217,7 @@ describe("InMemoryCollectionRoutingMap Tests", function() {
|
|||
2
|
||||
]
|
||||
];
|
||||
let collectionRoutingMap = CollectionRoutingMapFactory.createCompleteRoutingMap(
|
||||
partitionRangeWithInfo,
|
||||
"sample collection id"
|
||||
);
|
||||
let collectionRoutingMap = createCompleteRoutingMap(partitionRangeWithInfo, "sample collection id");
|
||||
assert.equal(collectionRoutingMap, null);
|
||||
|
||||
partitionRangeWithInfo = [
|
||||
|
@ -249,10 +238,7 @@ describe("InMemoryCollectionRoutingMap Tests", function() {
|
|||
2
|
||||
]
|
||||
];
|
||||
collectionRoutingMap = CollectionRoutingMapFactory.createCompleteRoutingMap(
|
||||
partitionRangeWithInfo,
|
||||
"sample collection id"
|
||||
);
|
||||
collectionRoutingMap = createCompleteRoutingMap(partitionRangeWithInfo, "sample collection id");
|
||||
assert.notEqual(collectionRoutingMap, null);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,12 +2,12 @@ import assert from "assert";
|
|||
import * as os from "os";
|
||||
import * as util from "util";
|
||||
import * as packageJson from "../../../package.json";
|
||||
import { Platform } from "../../common";
|
||||
import { getSafeUserAgentSegmentInfo, getUserAgent } from "../../common/index";
|
||||
import { Constants } from "../../index";
|
||||
|
||||
describe("Platform.getUserAgent", function() {
|
||||
describe("getUserAgent", function() {
|
||||
it("getUserAgent()", function() {
|
||||
const userAgent = Platform.getUserAgent();
|
||||
const userAgent = getUserAgent();
|
||||
const expectedUserAgent = util.format(
|
||||
"%s/%s Nodejs/%s azure-cosmos-js/%s",
|
||||
os.platform(),
|
||||
|
@ -18,21 +18,21 @@ describe("Platform.getUserAgent", function() {
|
|||
assert.strictEqual(userAgent, expectedUserAgent, "invalid UserAgent format");
|
||||
});
|
||||
|
||||
describe("Platform._getSafeUserAgentSegmentInfo()", function() {
|
||||
describe("Platform.getSafeUserAgentSegmentInfo()", function() {
|
||||
it("Removing spaces", function() {
|
||||
const safeString = Platform._getSafeUserAgentSegmentInfo("a b c");
|
||||
const safeString = getSafeUserAgentSegmentInfo("a b c");
|
||||
assert.strictEqual(safeString, "abc");
|
||||
});
|
||||
it("empty string handling", function() {
|
||||
const safeString = Platform._getSafeUserAgentSegmentInfo("");
|
||||
const safeString = getSafeUserAgentSegmentInfo("");
|
||||
assert.strictEqual(safeString, "unknown");
|
||||
});
|
||||
it("undefined", function() {
|
||||
const safeString = Platform._getSafeUserAgentSegmentInfo(undefined);
|
||||
const safeString = getSafeUserAgentSegmentInfo(undefined);
|
||||
assert.strictEqual(safeString, "unknown");
|
||||
});
|
||||
it("null", function() {
|
||||
const safeString = Platform._getSafeUserAgentSegmentInfo(null);
|
||||
const safeString = getSafeUserAgentSegmentInfo(null);
|
||||
assert.strictEqual(safeString, "unknown");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
"object-literal-sort-keys": false,
|
||||
"member-ordering": false, // TODO: might want to look at this eventually...
|
||||
"no-floating-promises": true,
|
||||
"import-blacklist": [true, "assert", "util"]
|
||||
"import-blacklist": [true, "assert", "util"],
|
||||
"no-unnecessary-class": true,
|
||||
"no-invalid-this": true
|
||||
},
|
||||
"linterOptions": {
|
||||
"exclude": ["*.json"]
|
||||
|
|
Загрузка…
Ссылка в новой задаче