Enable no-unecessary-class (v3) (#211)

Supersedes #32
This commit is contained in:
Steve Faulkner 2018-12-18 20:25:59 -05:00 коммит произвёл GitHub
Родитель fffca68542
Коммит 5526cb131f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
55 изменённых файлов: 1182 добавлений и 1256 удалений

Просмотреть файл

@ -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];
}
}

Просмотреть файл

@ -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"]