@azure/cosmos
Version:
Microsoft Azure Cosmos DB Service Node.js SDK for SQL API
289 lines • 9.17 kB
JavaScript
import { OperationType } from "./constants";
const trimLeftSlashes = new RegExp("^[/]+");
const trimRightSlashes = new RegExp("[/]+$");
const illegalResourceIdCharacters = new RegExp("[/\\\\?#]");
const illegalItemResourceIdCharacters = new RegExp("[/\\\\#]");
/** @hidden */
export function jsonStringifyAndEscapeNonASCII(arg) {
// TODO: better way for this? Not sure.
// escapes non-ASCII characters as \uXXXX
return JSON.stringify(arg).replace(/[\u007F-\uFFFF]/g, (m) => {
return "\\u" + ("0000" + m.charCodeAt(0).toString(16)).slice(-4);
});
}
/**
* @hidden
*/
export function parseLink(resourcePath) {
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[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]/ ....
/[resourceType]/[resourceId]/
The result of split will be in the form of
[[[resourceType], [resourceId] ... ,[resourceType], [resourceId], ""]
In the first case, to extract the resourceId it will the element before last ( at length -2 )
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;
}
/**
* @hidden
*/
export function isReadRequest(operationType) {
return operationType === OperationType.Read || operationType === OperationType.Query;
}
/**
* @hidden
*/
export function sleep(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
}
/**
* @hidden
*/
export function getContainerLink(link) {
return link.split("/").slice(0, 4).join("/");
}
/**
* @hidden
*/
export function trimSlashes(source) {
return source.replace(trimLeftSlashes, "").replace(trimRightSlashes, "");
}
/**
* @hidden
*/
export function getHexaDigit() {
return Math.floor(Math.random() * 16).toString(16);
}
/**
* @hidden
*/
export function parsePath(path) {
const pathParts = [];
let currentIndex = 0;
const throwError = () => {
throw new Error("Path " + path + " is invalid at index " + currentIndex);
};
const getEscapedToken = () => {
const quote = path[currentIndex];
let newIndex = ++currentIndex;
for (;;) {
newIndex = path.indexOf(quote, newIndex);
if (newIndex === -1) {
throwError();
}
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;
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) {
break;
}
if (path[currentIndex] === '"' || path[currentIndex] === "'") {
pathParts.push(getEscapedToken());
}
else {
pathParts.push(getToken());
}
}
return pathParts;
}
/**
* @hidden
*/
export function isResourceValid(resource, err) {
// TODO: fix strictness issues so that caller contexts respects the types of the functions
if (resource.id) {
if (typeof resource.id !== "string") {
err.message = "Id must be a string.";
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;
}
/**
* @hidden
*/
export function isItemResourceValid(resource, err) {
// TODO: fix strictness issues so that caller contexts respects the types of the functions
if (resource.id) {
if (typeof resource.id !== "string") {
err.message = "Id must be a string.";
return false;
}
if (resource.id.indexOf("/") !== -1 ||
resource.id.indexOf("\\") !== -1 ||
resource.id.indexOf("#") !== -1) {
err.message = "Id contains illegal chars.";
return false;
}
}
return true;
}
/** @hidden */
export function getIdFromLink(resourceLink) {
resourceLink = trimSlashes(resourceLink);
return resourceLink;
}
/** @hidden */
export function getPathFromLink(resourceLink, resourceType) {
resourceLink = trimSlashes(resourceLink);
if (resourceType) {
return "/" + encodeURI(resourceLink) + "/" + resourceType;
}
else {
return "/" + encodeURI(resourceLink);
}
}
/**
* @hidden
*/
export function isStringNullOrEmpty(inputString) {
// checks whether string is null, undefined, empty or only contains space
return !inputString || /^\s*$/.test(inputString);
}
/**
* @hidden
*/
export function trimSlashFromLeftAndRight(inputString) {
if (typeof inputString !== "string") {
throw new Error("invalid input: input is not string");
}
return inputString.replace(trimLeftSlashes, "").replace(trimRightSlashes, "");
}
/**
* @hidden
*/
export function validateResourceId(resourceId) {
// 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 resource id contains illegal characters throw an error
if (illegalResourceIdCharacters.test(resourceId)) {
throw new Error("Illegal characters ['/', '\\', '#', '?'] cannot be used in Resource ID");
}
return true;
}
/**
* @hidden
*/
export function validateItemResourceId(resourceId) {
// 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 resource id contains illegal characters throw an error
if (illegalItemResourceIdCharacters.test(resourceId)) {
throw new Error("Illegal characters ['/', '\\', '#'] cannot be used in Resource ID");
}
return true;
}
/**
* @hidden
*/
export function getResourceIdFromPath(resourcePath) {
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];
}
/**
* @hidden
*/
export function parseConnectionString(connectionString) {
const keyValueStrings = connectionString.split(";");
const { AccountEndpoint, AccountKey } = keyValueStrings.reduce((connectionObject, keyValueString) => {
const [key, ...value] = keyValueString.split("=");
connectionObject[key] = value.join("=");
return connectionObject;
}, {});
if (!AccountEndpoint || !AccountKey) {
throw new Error("Could not parse the provided connection string");
}
return {
endpoint: AccountEndpoint,
key: AccountKey,
};
}
//# sourceMappingURL=helper.js.map