diff --git a/source/DocumentDB.njsproj b/source/DocumentDB.njsproj index f38aef0..9f642ec 100644 --- a/source/DocumentDB.njsproj +++ b/source/DocumentDB.njsproj @@ -37,6 +37,7 @@ + diff --git a/source/Gruntfile.js b/source/Gruntfile.js index 2918f19..8dc4034 100644 --- a/source/Gruntfile.js +++ b/source/Gruntfile.js @@ -1,3 +1,26 @@ +/* +The MIT License (MIT) +Copyright (c) 2014 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + "use strict"; module.exports = function (grunt) { diff --git a/source/index.js b/source/index.js index a162f3a..d72aa83 100644 --- a/source/index.js +++ b/source/index.js @@ -1 +1,24 @@ +/* +The MIT License (MIT) +Copyright (c) 2014 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + module.exports = require('./lib/'); \ No newline at end of file diff --git a/source/lib/auth.js b/source/lib/auth.js index bb433d8..fecb53a 100644 --- a/source/lib/auth.js +++ b/source/lib/auth.js @@ -1,6 +1,25 @@ -//---------------------------------------------------------------------------- -// Copyright (c) Microsoft Corporation. All rights reserved. -//---------------------------------------------------------------------------- +/* +The MIT License (MIT) +Copyright (c) 2014 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ "use strict"; diff --git a/source/lib/base.js b/source/lib/base.js index cd1536a..c5025c4 100644 --- a/source/lib/base.js +++ b/source/lib/base.js @@ -1,6 +1,25 @@ -//---------------------------------------------------------------------------- -// Copyright (c) Microsoft Corporation. All rights reserved. -//---------------------------------------------------------------------------- +/* +The MIT License (MIT) +Copyright (c) 2014 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ "use strict"; diff --git a/source/lib/constants.js b/source/lib/constants.js index 2111635..1e4bb3e 100644 --- a/source/lib/constants.js +++ b/source/lib/constants.js @@ -1,6 +1,26 @@ -//---------------------------------------------------------------------------- -// Copyright (c) Microsoft Corporation. All rights reserved. -//---------------------------------------------------------------------------- +/* +The MIT License (MIT) +Copyright (c) 2014 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + //SCRIPT START diff --git a/source/lib/documentclient.js b/source/lib/documentclient.js index c3051c5..55cc3a0 100644 --- a/source/lib/documentclient.js +++ b/source/lib/documentclient.js @@ -1,6 +1,25 @@ -//---------------------------------------------------------------------------- -// Copyright (c) Microsoft Corporation. All rights reserved. -//---------------------------------------------------------------------------- +/* +The MIT License (MIT) +Copyright (c) 2014 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ "use strict"; diff --git a/source/lib/documents.js b/source/lib/documents.js index 0eca635..5d10b05 100644 --- a/source/lib/documents.js +++ b/source/lib/documents.js @@ -1,6 +1,25 @@ -//---------------------------------------------------------------------------- -// Copyright (c) Microsoft Corporation. All rights reserved. -//---------------------------------------------------------------------------- +/* +The MIT License (MIT) +Copyright (c) 2014 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ "use strict"; diff --git a/source/lib/index.js b/source/lib/index.js index 3abf2db..c8f73e1 100644 --- a/source/lib/index.js +++ b/source/lib/index.js @@ -1,10 +1,36 @@ +/* +The MIT License (MIT) +Copyright (c) 2014 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + "use strict"; -var Client = require("./documentclient"); +var Client = require("./documentclient") + , Range = require("./range"); if (typeof exports !== "undefined") { exports.DocumentClient = Client.DocumentClient; exports.DocumentBase = Client.DocumentBase; exports.Base = Client.Base; exports.Constants = Client.Constants; + exports.Range = Range.Range; + exports.RangePartitionResolver = Range.RangePartitionResolver; } \ No newline at end of file diff --git a/source/lib/queryIterator.js b/source/lib/queryIterator.js index 329ed02..7341a9e 100644 --- a/source/lib/queryIterator.js +++ b/source/lib/queryIterator.js @@ -1,6 +1,25 @@ -//---------------------------------------------------------------------------- -// Copyright (c) Microsoft Corporation. All rights reserved. -//---------------------------------------------------------------------------- +/* +The MIT License (MIT) +Copyright (c) 2014 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ "use strict"; diff --git a/source/lib/range.js b/source/lib/range.js new file mode 100644 index 0000000..a5e7531 --- /dev/null +++ b/source/lib/range.js @@ -0,0 +1,308 @@ +/* +The MIT License (MIT) +Copyright (c) 2014 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +"use strict"; + +var Base = require("./base"); + +//SCRIPT START +var Range = Base.defineClass( + /** + * Represents a range object used by the RangePartitionResolver + * @constructor Range + * @param {object} options - The Range constructor options. + * @param {any} options.low - The low value in the range. + * @param {any} options.high - The high value in the range. + * @param {function} options.compareFunction - Optional function that accepts two arguments x and y and returns a negative value if x < y, zero if x = y, or a positive value if x > y. + **/ + function(options) { + if (options === undefined) { + options = {}; + } + if (options === null) { + throw new Error("Invalid argument: 'options' is null"); + } + if (typeof options !== "object") { + throw new Error("Invalid argument: 'options' is not an object"); + } + if (options.high === undefined) { + options.high = options.low; + } + this.low = options.low; + this.high = options.high; + this.compareFunction = options.compareFunction; + if (this.compareFunction !== undefined && typeof this.compareFunction !== "function") { + throw new Error("Invalid argument: 'options.compareFunction' is not a function"); + } + if (this._compare(this.low, this.high) > 0) { + throw new Error("Invalid argument: 'options.low' must be less than or equal than 'options.high'"); + } + Object.freeze(this); + }, + { + /** + * Compares two ranges + * @param {object} other - The input range to be compared with this range. + * @returns {number} - A negative value if this < other zero if this = other, or a positive value if this > other. + **/ + compareTo: function(other) { + if (this._compare(this.low, other.low) === 0 && this._compare(this.high, other.high) === 0) { + return 0; + } + if (this._compare(this.low, other.low) < 0 || this._compare(this.high, other.high) < 0) { + return -1; + } + return 1; + }, + + /** + * Check for containment of the other range in this range + * @param {object} other - The key or range to be checked if in this range. + * @returns {boolean} - Returns true if the input is contained in this range; false otherwise. + **/ + contains: function (other) { + if (Range._isRange(other)) { + return this._containsRange(other); + } + else { + return this._containsPoint(other); + } + }, + + /** + * Checks if the other range intersects with this range. + * @param {object} other - The input range to be checked for intersection with this range. + * @returns {boolean} - Returns true if the two ranges intersect with each other; false otherwise. + **/ + intersect: function (other) { + if (other === undefined || other === null) { + throw new Error("Invalid Argument: 'other' is undefined or null"); + } + var maxLow = this._compare(this.low, other.low) >= 0 ? this.low : other.low; + var minHigh = this._compare(this.high, other.high) <= 0 ? this.high : other.high; + if (this._compare(maxLow, minHigh) <= 0) { + return true; + } + return false; + }, + + /** + * Converts the range to a string in the form of "low,high" + * @returns {string} - Returns a string representation of the range. + **/ + toString: function () { + return String(this.low) + "," + String(this.high); + }, + + /** @ignore */ + _compare: function (x, y) { + // Same semantics as Array.sort + // http://www.ecma-international.org/ecma-262/6.0/#sec-sortcompare + if (x === undefined && y === undefined) + return 0; + if (x === undefined) + return 1; + if (y === undefined) + return -1; + if (this.compareFunction !== undefined) { + var v = Number(this.compareFunction(x, y)); + if (v === NaN) + return 0; + return v; + } + var xString = String(x); + var yString = String(y); + if (xString < yString) + return -1; + if (xString > yString) + return 1; + return 0; + }, + + /** @ignore */ + _containsPoint: function (point) { + if (this._compare(point, this.low) >= 0 && this._compare(point, this.high) <= 0) { + return true; + } + return false; + }, + + /** @ignore */ + _containsRange: function (other) { + if (this._compare(other.low, this.low) >= 0 && this._compare(other.high, this.high) <= 0) { + return true; + } + return false; + }, + + /** @ignore */ + _toArrayImplementation: function(callback){ + var that = this; + if (this._canFetchMore()) { + this._fetchMore(function(err, resources, headers){ + if(err) { + return callback(err, undefined, headers); + } + + that.resHeaders = headers; + that.resources = that.resources.concat(resources); + that._toArrayImplementation(callback); + }); + } else { + this._state = this._states.ended; + callback(undefined, this.resources, this.resHeaders); + } + } + }, + { + /** @ignore */ + _isRange: function (pointOrRange) { + if (pointOrRange === undefined) { + return false; + } + if (pointOrRange === null) { + return false; + } + if (typeof pointOrRange !== "object") { + return false; + } + return ("low" in pointOrRange && "high" in pointOrRange); + } + } +); + +var RangePartitionResolver = Base.defineClass( + /** + * RangePartitionResolver implements partitioning using a partition map of ranges of values to a collection link. + * @param {string or function} partitionKeyExtractor - If partitionKeyExtractor is a string, it should be the name of the property in the document to execute the hashing on. + * If partitionKeyExtractor is a function, it should be a function to extract the partition key from any object. + * @param {Array} partitionKeyMap - The map from range to collection-link that is used for partitioning requests. + **/ + function(partitionKeyExtractor, partitionKeyMap) { + if (partitionKeyExtractor === undefined || partitionKeyExtractor === null) { + throw new Error("partitionKeyExtractor cannot be null or undefined"); + } + if (typeof partitionKeyExtractor !== "string" && typeof partitionKeyExtractor !== "function") { + throw new Error("partitionKeyExtractor has to have 'string' or 'function' type."); + } + if (partitionKeyMap === undefined || partitionKeyMap === null) { + throw new Error("partitionKeyMap cannot be null or undefined"); + } + if (!(Array.isArray(partitionKeyMap))) { + throw new Error("partitionKeyMap has to be an Array"); + } + var allMapEntriesAreValid = partitionKeyMap.every(function (mapEntry) { + if ((mapEntry === undefined) || mapEntry === null) { + return false; + } + if (mapEntry.range === undefined) { + return false; + } + if (!(mapEntry.range instanceof Range)) { + return false; + } + if (mapEntry.link === undefined) { + return false; + } + if (typeof mapEntry.link !== "string") { + return false; + } + return true; + }); + if (!allMapEntriesAreValid) { + throw new Error("All partitionKeyMap entries have to be a tuple {range: Range, link: string }"); + } + this.partitionKeyExtractor = partitionKeyExtractor; + this.partitionKeyMap = partitionKeyMap; + }, { + /** + * Extracts the partition key from the specified document using the partitionKeyExtractor + * @param {object} document - The document from which to extract the partition key. + * @returns {} + **/ + getPartitionKey: function (document) { + if (typeof this.partitionKeyExtractor === "string") { + return document[this.partitionKeyExtractor]; + } + if (typeof this.partitionKeyExtractor === "function") { + return this.partitionKeyExtractor(document); + } + throw new Error("Unable to extract partition key from document. Ensure PartitionKeyExtractor is a valid function or property name."); + }, + + /** + * Given a partition key, returns the correct collection link for creating a document using the range partition map. + * @param {any} partitionKey - The partition key used to determine the target collection for create + * @returns {string} - The target collection link that will be used for document creation. + **/ + resolveForCreate: function (partitionKey) { + var range = new Range({ low: partitionKey }); + var mapEntry = this._getFirstContainingMapEntryOrNull(range); + if (mapEntry !== undefined && mapEntry !== null) { + return mapEntry.link; + } + throw new Error("Invalid operation: A containing range for '" + range.toString() + "' doesn't exist in the partition map."); + }, + + /** + * Given a partition key, returns a list of collection links to read from using the range partition map. + * @param {any} partitionKey - The partition key used to determine the target collection for query + * @returns {string[]} - The list of target collection links. + **/ + resolveForRead: function (partitionKey) { + if (partitionKey === undefined || partitionKey === null) { + return this.partitionKeyMap.map(function (i) { return i.link; }); + } + else { + return this._getIntersectingMapEntries(partitionKey).map(function (i) { return i.link; }); + } + }, + + /** @ignore */ + _getFirstContainingMapEntryOrNull: function (point) { + var containingMapEntries = this.partitionKeyMap.filter(function (p) { return p.range !== undefined && p.range.contains(point); }); + if (containingMapEntries && containingMapEntries.length > 0) { + return containingMapEntries[0]; + } + return null; + }, + + /** @ignore */ + _getIntersectingMapEntries: function (partitionKey) { + var _this = this; + var partitionKeys = (partitionKey instanceof Array) ? partitionKey : [partitionKey]; + var ranges = partitionKeys.map(function (p) { return Range._isRange(p) ? p : new Range(p); }); + var result = new Array(); + ranges.forEach(function (range) { + result.concat(_this.partitionKeyMap.filter(function (entry) { return entry.range.intersect(range); })); + }); + return result; + } + } +); +//SCRIPT END + +if (typeof exports !== "undefined") { + exports.Range = Range; + exports.RangePartitionResolver = RangePartitionResolver; +} \ No newline at end of file diff --git a/source/lib/request.js b/source/lib/request.js index 2ec94bb..21d0301 100644 --- a/source/lib/request.js +++ b/source/lib/request.js @@ -1,6 +1,25 @@ -//---------------------------------------------------------------------------- -// Copyright (c) Microsoft Corporation. All rights reserved. -//---------------------------------------------------------------------------- +/* +The MIT License (MIT) +Copyright (c) 2014 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ "use strict"; diff --git a/source/test/_testConfig.js b/source/test/_testConfig.js index f24a4b4..53c2586 100644 --- a/source/test/_testConfig.js +++ b/source/test/_testConfig.js @@ -1,5 +1,28 @@ -var host = "MyHost"; -var key = "MyKey"; +/* +The MIT License (MIT) +Copyright (c) 2014 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +var masterKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; +var host = "https://localhost:443"; exports.host = host; -exports.masterKey = key; +exports.masterKey = masterKey; diff --git a/source/test/test.js b/source/test/test.js index d845f14..d48d206 100644 --- a/source/test/test.js +++ b/source/test/test.js @@ -1,6 +1,25 @@ -//---------------------------------------------------------------------------- -// Copyright (c) Microsoft Corporation. All rights reserved. -//---------------------------------------------------------------------------- +/* +The MIT License (MIT) +Copyright (c) 2014 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ "use strict"; @@ -10,7 +29,9 @@ var Base = require("documentdb").Base , Constants = require("documentdb").Constants , assert = require("assert") , testConfig = require("./_testConfig") - , Stream = require("stream"); + , Stream = require("stream") + , Range = require("documentdb").Range + , RangePartitionResolver = require("documentdb").RangePartitionResolver; process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; @@ -2477,4 +2498,740 @@ describe("NodeJS CRUD Tests", function() { done(); }); }); + + describe("Range Tests", function () { + describe("constructor", function () { + var invalidOptionsTest = function (options, expectedError, done) { + assert.throws( + function () { + var r = new Range(options); + }, + expectedError); + done(); + } + + var optionsIsNullTest = function (options, done) { + invalidOptionsTest(options, /Invalid argument: 'options' is null/, done); + } + + var optionsIsNotAnObjectTest = function (options, done) { + invalidOptionsTest(options, /Invalid argument: 'options' is not an object/, done); + } + + var comparisonFunctionIsNotAFunctionTest = function (options, done) { + invalidOptionsTest(options, /Invalid argument: 'options.compareFunction' is not a function/, done); + } + + var invalidRangeTest = function (options, done) { + invalidOptionsTest(options, /Invalid argument: 'options.low' must be less than or equal than 'options.high'/, done); + } + + it("[nativeApi] options - undefined (ommited argument)", function (done) { + assert(new Range()); + done(); + }); + + it("[nativeApi] options - undefined (literal argument)", function (done) { + assert(new Range(undefined)); + done(); + }); + + it("[nativeApi] options - null ", function (done) { + var options = null; + optionsIsNullTest(options, done); + }); + + it("[nativeApi] options - number", function (done) { + var options = 0; + optionsIsNotAnObjectTest(options, done); + }); + + it("[nativeApi] invalid options - string", function (done) { + var options = ""; + optionsIsNotAnObjectTest(options, done); + }); + + it("[nativeApi] invalid options - boolean", function (done) { + var options = false; + optionsIsNotAnObjectTest(options, done); + }); + + it("[nativeApi] invalid compareFunction - null", function (done) { + var options = { compareFunction: null }; + comparisonFunctionIsNotAFunctionTest(options, done); + }); + + it("[nativeApi] invalid compareFunction - string", function (done) { + var options = { compareFunction: "" }; + comparisonFunctionIsNotAFunctionTest(options, done); + }); + + it("[nativeApi] invalid compareFunction - number", function (done) { + var options = { compareFunction: 0 }; + comparisonFunctionIsNotAFunctionTest(options, done); + }); + + it("[nativeApi] invalid compareFunction - boolean", function (done) { + var options = { compareFunction: false }; + comparisonFunctionIsNotAFunctionTest(options, done); + }); + + it("[nativeApi] invalid compareFunction - object", function (done) { + var options = { compareFunction: {} }; + comparisonFunctionIsNotAFunctionTest(options, done); + }); + + it("[nativeApi] invalid range - low greater than high", function (done) { + var options = { low: 2, high: 1 }; + invalidRangeTest(options, done); + }); + + it("[nativeApi] compareFunction exception is thrown - inside constructor ", function (done) { + var options = { + low: 0, + high: 1, + compareFunction: function (a, b) { + throw new Error("Compare error"); + } + }; + assert.throws( + function () { + var r = new Range(options); + }, + /Error: Compare error/); + done(); + }); + + it("[nativeApi] compareFunction exception is thrown - outside constructor ", function (done) { + var options = { + low: 0, + high: 1, + compareFunction: function (a, b) { + if (a === 1) { + throw new Error("Compare error"); + } + } + }; + var r = new Range(options); + assert.throws( + function () { + r._compare(1, 0); + }, + /Error: Compare error/); + done(); + }); + + it("[nativeApi] Range instances are frozen", function (done) { + var r = new Range(); + + assert.throws( + function () { + r.compareFunction = 1; + }, + /Cannot assign to read only property 'compareFunction' of \[object Object\]/ + ); + + done(); + }); + + }); + + describe("contains", function () { + it("[nativeApi] undefined,undefined contains undefined is true", function (done) { + var r = new Range(); + assert(r.contains(undefined)); + done(); + }); + + it("[nativeApi] undefined,undefined contains null is false", function (done) { + var r = new Range(); + assert(!r.contains(null)); + done(); + }); + + it("[nativeApi] null,null contains undefined is true", function (done) { + var r = new Range({ low: null }); + assert(r.contains(null)); + done(); + }); + + it("[nativeApi] null,null contains null is true", function (done) { + var r = new Range({ low: null }); + assert(r.contains(null)); + done(); + }); + + it("[nativeApi] range contains self is true - default range", function (done) { + var r = new Range(); + assert(r.contains(r)); + done(); + }); + + it("[nativeApi] range contains self is true - non-default range", function (done) { + var r = new Range({ low: "A" }); + assert(r.contains(r)); + done(); + }); + + it("[nativeApi] A,D contains B,C is true", function (done) { + var r1 = new Range({ low: "A", high: "D" }); + var r2 = new Range({ low: "B", high: "C" }); + assert(r1.contains(r2)); + done(); + }); + + it("[nativeApi] B,C contains A,D is false", function (done) { + var r1 = new Range({ low: "B", high: "C" }); + var r2 = new Range({ low: "A", high: "D" }); + assert(!r1.contains(r2)); + done(); + }); + + it("[nativeApi] A,C contains B,D is false", function (done) { + var r1 = new Range({ low: "A", high: "C" }); + var r2 = new Range({ low: "B", high: "D" }); + assert(!r1.contains(r2)); + done(); + }); + + it("[nativeApi] B,D contains A,C is false", function (done) { + var r1 = new Range({ low: "B", high: "D" }); + var r2 = new Range({ low: "A", high: "C" }); + assert(!r1.contains(r2)); + done(); + }); + + it("[nativeApi] A,B contains B,C is false", function (done) { + var r1 = new Range({ low: "A", high: "B" }); + var r2 = new Range({ low: "B", high: "C" }); + assert(!r1.contains(r2)); + done(); + }); + + it("[nativeApi] B,C contains A,B is false", function (done) { + var r1 = new Range({ low: "B", high: "C" }); + var r2 = new Range({ low: "A", high: "B" }); + assert(!r1.contains(r2)); + done(); + }); + + it("[nativeApi] A,B contains C,D is false", function (done) { + var r1 = new Range({ low: "A", high: "B" }); + var r2 = new Range({ low: "C", high: "D" }); + assert(!r1.contains(r2)); + done(); + }); + + it("[nativeApi] C,D contains A,B is false", function (done) { + var r1 = new Range({ low: "C", high: "D" }); + var r2 = new Range({ low: "A", high: "B" }); + assert(!r1.contains(r2)); + done(); + }); + + it("[nativeApi] A,C contains B is true", function (done) { + var r1 = new Range({ low: "A", high: "C" }); + assert(r1.contains("B")); + done(); + }); + + it("[nativeApi] B,C contains A is false", function (done) { + var r1 = new Range({ low: "B", high: "C" }); + assert(!r1.contains("A")); + done(); + }); + + it("[nativeApi] A,B contains C is false", function (done) { + var r1 = new Range({ low: "A", high: "B" }); + assert(!r1.contains("C")); + done(); + }); + }); + + describe("intersect", function () { + var otherIsUndefinedOrNullTest = function (other, done) { + var r = new Range(); + assert.throws( + function () { + r.intersect(other); + }, + /Invalid Argument: 'other' is undefined or null/ + ); + done(); + }; + + it("[nativeApi] error - other is undefined", function (done) { + otherIsUndefinedOrNullTest(undefined, done); + }); + + it("[nativeApi] error - other is null", function (done) { + otherIsUndefinedOrNullTest(null, done); + }); + + it("[nativeApi] range intersect self is true - default range", function (done) { + var r = new Range(); + assert(r.intersect(r)); + done(); + }); + + it("[nativeApi] R intersect R is true - non default range", function (done) { + var r = new Range({ low: 1, high: "2" }); + assert(r.intersect(r)); + done(); + }); + + it("[nativeApi] A,D insersects B,C is true", function (done) { + var r1 = new Range({ low: "A", high: "D" }); + var r2 = new Range({ low: "B", high: "C" }); + assert(r1.intersect(r2)); + done(); + }); + + it("[nativeApi] B,C insersects A,D is true", function (done) { + var r1 = new Range({ low: "B", high: "C" }); + var r2 = new Range({ low: "A", high: "D" }); + assert(r1.intersect(r2)); + done(); + }); + + it("[nativeApi] A,C insersects B,D is true", function (done) { + var r1 = new Range({ low: "A", high: "C" }); + var r2 = new Range({ low: "B", high: "D" }); + assert(r1.intersect(r2)); + assert(r2.intersect(r1)); + done(); + }); + + it("[nativeApi] B,D insersects A,C is true", function (done) { + var r1 = new Range({ low: "B", high: "D" }); + var r2 = new Range({ low: "A", high: "C" }); + assert(r1.intersect(r2)); + done(); + }); + + it("[nativeApi] A,B insersects B,C is true", function (done) { + var r1 = new Range({ low: "A", high: "B" }); + var r2 = new Range({ low: "B", high: "C" }); + assert(r1.intersect(r2)); + assert(r2.intersect(r1)); + done(); + }); + + it("[nativeApi] B,C insersects A,B is true", function (done) { + var r1 = new Range({ low: "B", high: "C" }); + var r2 = new Range({ low: "A", high: "B" }); + assert(r1.intersect(r2)); + done(); + }); + + it("[nativeApi] A,B insersects C,D is false", function (done) { + var r1 = new Range({ low: "A", high: "B" }); + var r2 = new Range({ low: "C", high: "D" }); + assert(!r1.intersect(r2)); + done(); + }); + + it("[nativeApi] C,D insersects A,B is false", function (done) { + var r1 = new Range({ low: "C", high: "D" }); + var r2 = new Range({ low: "A", high: "B" }); + assert(!r1.intersect(r2)); + done(); + }); + }); + + describe("toString", function () { + var toStringTest = function (options, expectedString, done) { + var r = new Range(options); + assert.strictEqual(r.toString(), expectedString); + done(); + }; + + it("[nativeApi] undefined values", function (done) { + toStringTest(undefined, "undefined,undefined", done); + }); + it("[nativeApi] null values", function (done) { + toStringTest({ low: null }, "null,null", done); + }); + it("[nativeApi] NaN values", function (done) { + toStringTest({ low: NaN }, "NaN,NaN", done); + }); + it("[nativeApi] number values", function (done) { + toStringTest({ low: 1 }, "1,1", done); + }); + it("[nativeApi] string values", function (done) { + toStringTest({ low: "a" }, "a,a", done); + }); + it("[nativeApi] boolean values", function (done) { + toStringTest({ low: false, high: true }, "false,true", done); + }); + it("[nativeApi] object values", function (done) { + toStringTest({ low: {} }, "[object Object],[object Object]", done); + }); + }); + + describe("_compare", function () { + var r = new Range(); + + var rangeWithComparisonAsNumbers = new Range({ + compareFunction: function (a, b) { + return a - b; + } + }); + + var rangeWithConstantComparison = new Range({ + compareFunction: function (a, b) { + return 0; + } + }); + + + it("[nativeApi] _compare(undefined, undefined) === 0", function (done) { + assert(r._compare() === 0); + assert(r._compare(undefined) === 0); + assert(r._compare(undefined, undefined) === 0); + done(); + }); + + it("[nativeApi] _compare(undefined, y) > 0", function (done) { + assert(r._compare(undefined, null) > 0); + assert(r._compare(undefined, -NaN) > 0); + assert(r._compare(undefined, 0) > 0); + assert(r._compare(undefined, NaN) > 0); + assert(r._compare(undefined, true) > 0); + assert(r._compare(undefined, false) > 0); + assert(r._compare(undefined, "a") > 0); + assert(r._compare(undefined, "undefined") > 0); + assert(r._compare(undefined, "z") > 0); + assert(r._compare(undefined, []) > 0); + assert(r._compare(undefined, {}) > 0); + done(); + }); + + it("[nativeApi] _compare(x, undefined) < -1", function (done) { + assert(r._compare(null) < 0); + assert(r._compare(-NaN) < 0); + assert(r._compare(0) < 0); + assert(r._compare(NaN) < 0); + assert(r._compare(true) < 0); + assert(r._compare(false) < 0); + assert(r._compare("a") < 0); + assert(r._compare("undefined") < 0); + assert(r._compare("z") < 0); + assert(r._compare([]) < 0); + assert(r._compare({}) < 0); + done(); + }); + + it("[nativeApi] _compare values as strings (default)", function (done) { + assert(r._compare("A", "B") < 0); + assert(r._compare("", "") === 0); + assert(r._compare("B", "A") > 0); + assert(r._compare("10", "2") < 0); + assert(r._compare(10, "02") > 0); + assert(r._compare(10, 2) < 0); + assert(r._compare(null, "nulm") < 0); + assert(r._compare(null, "null") === 0); + assert(r._compare(null, "nulk") > 0); + assert(r._compare(true, "truf") < 0); + assert(r._compare(true, "true") === 0); + assert(r._compare(true, "trud") > 0); + assert(r._compare({}, "[object Object]") === 0); + done(); + }); + + it("[nativeApi] _compare values as numbers", function (done) { + assert(rangeWithComparisonAsNumbers._compare(undefined, 2) > 0); + assert(rangeWithComparisonAsNumbers._compare(1, 2) < 0); + assert(rangeWithComparisonAsNumbers._compare(0, 0) === 0); + assert(rangeWithComparisonAsNumbers._compare(10, 2) > 0); + done(); + }); + + it("[nativeApi] _compare always return 0", function (done) { + assert(rangeWithConstantComparison._compare(1, 2) === 0); + assert(rangeWithConstantComparison._compare(2, 1) === 0); + assert(rangeWithConstantComparison._compare(undefined, 2) > 0); + assert(rangeWithConstantComparison._compare(1, undefined) < 0); + + done(); + }); + }); + + describe("_isRange", function () { + it("[nativeApi] _isRange(undefined) is false", function (done) { + assert(!Range._isRange()); + done(); + }); + + it("[nativeApi] _isRange(null) is false", function (done) { + assert(!Range._isRange(null)); + done(); + }); + + it("[nativeApi] _isRange(non-object) is false", function (done) { + var points = [ + undefined, + null, + 1, + "", + true, + NaN, + function () { + }, + {}, + { + low: "" + } + ]; + + for (var i = 0; i < points.length; i++) { + assert(!Range._isRange(points[i])); + } + + done(); + }); + + it("[nativeApi] _isRange(point) is false", function (done) { + var ranges = [ + { + low: "", + high: 1 + }, + new Range() + ]; + + for (var i = 0; i < ranges.length; i++) { + assert(Range._isRange(ranges[i])); + } + + done(); + }); + }); + }); + + describe("RangePartitionResolver Tests", function () { + describe("constructor", function () { + it("[nativeApi] missing partitionKeyExtractor throws", function (done) { + var expetcedError = /Error: partitionKeyExtractor cannot be null or undefined/; + + assert.throws( + function () { + var r = new RangePartitionResolver(); + }, + expetcedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver(undefined); + }, + expetcedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver(null); + }, + expetcedError + ); + + done(); + }); + + it("[nativeApi] invalid partitionKeyExtractor throws", function (done) { + var expetcedError = /Error: partitionKeyExtractor has to have 'string' or 'function' type/; + + assert.throws( + function () { + var r = new RangePartitionResolver(0); + }, + expetcedError + ); + + assert.throws( + function () { + var r = RangePartitionResolver(true); + }, + expetcedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver(NaN); + }, + expetcedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver([]); + }, + expetcedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver({}); + }, + expetcedError + ); + + + done(); + }); + + it("[nativeApi] missing partitionKeyMap throws", function (done) { + var expectedError = /Error: partitionKeyMap cannot be null or undefined/; + + assert.throws( + function () { + var r = new RangePartitionResolver(""); + }, + expectedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver(function () { + }); + }, + expectedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver("", null); + }, + expectedError + ); + + done(); + }); + + it("[nativeApi] invalid partitionKeyMap throws", function (done) { + var expectedError = /Error: partitionKeyMap has to be an Array/; + + assert.throws( + function () { + var r = new RangePartitionResolver("", 0); + }, + expectedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver("", ""); + }, + expectedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver("", true); + }, + expectedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver("", NaN); + }, + expectedError + ); + + assert.throws( + function () { + var r = new RangePartitionResolver("", {}); + }, + expectedError + ); + + var r = new RangePartitionResolver("", new Array()); + done(); + }); + + it("[nativeApi] valid RangePartitionResolver", function (done) { + var resolver = new RangePartitionResolver("", []); + assert(resolver); + assert.strictEqual(resolver.partitionKeyExtractor, ""); + assert.deepEqual(resolver.partitionKeyMap, []); + done(); + }); + }); + + describe("_getFirstContainingMapEntryOrNull", function () { + it("[nativeApi] _getFirstContainingMapEntryOrNull - empty map returns null", function (done) { + var ranges = [undefined, null, 0, "", true, [], {}, NaN, new Range()]; + var resolver = new RangePartitionResolver("", []); + ranges.forEach(function (r) { + var result = resolver._getFirstContainingMapEntryOrNull(r); + assert.equal(result, null); + }); + done(); + }); + + it("[nativeApi] _tryGetContainingRange - map with no containing entry returns null", function (done) { + var mapEntry = { range: new Range({ low: "A" }), link: "link1" }; + var resolver = new RangePartitionResolver("key", [mapEntry]); + var result = resolver._getFirstContainingMapEntryOrNull(new Range({ low: "B" })); + assert.equal(result, null); + done(); + }); + + it("[nativeApi] _tryGetContainingRange - map with single containing entry returns entry", function (done) { + var mapEntry = { range: new Range(), link: "link1" }; + var resolver = new RangePartitionResolver("key", [mapEntry]); + var result = resolver._getFirstContainingMapEntryOrNull(new Range()); + assert.deepEqual(result, { range: new Range(), link: "link1" }); + done(); + }); + + it("[nativeApi] _tryGetContainingRange - map with more multiple containing entries returns first entry", function (done) { + var map1 = [ + { range: new Range({ low: "A", high: "B" }), link: "link1" }, + { range: new Range({ low: "A" }), link: "link2" } + ]; + + var resolver1 = new RangePartitionResolver("key", map1); + var result1 = resolver1._getFirstContainingMapEntryOrNull(new Range({ low: "A" })); + assert.strictEqual(result1.link, "link1"); + + var map2 = [ + { range: new Range({ low: "A" }), link: "link2" }, + { range: new Range({ low: "A", high: "Z" }), link: "link1" } + ]; + + var resolver2 = new RangePartitionResolver("key", map2); + var result2 = resolver2._getFirstContainingMapEntryOrNull(new Range({ low: "A" })); + assert.strictEqual(result2.link, "link2"); + done(); + }); + }); + + describe("resolveForCreate", function () { + it("[nativeApi] _tryGetContainingRange - map containing parition key returns corresponding link", function (done) { + var resolver = new RangePartitionResolver("key", [ + { range: new Range({ low: "A", high: "M" }), link: "link1" }, + { range: new Range({ low: "N", high: "Z" }), link: "link2" } + ]); + var result = resolver.resolveForCreate("X"); + assert.strictEqual(result, "link2"); + done(); + }); + + it("[nativeApi] _tryGetContainingRange - map not containing parition key throws", function (done) { + var resolver = new RangePartitionResolver("key", [ + { range: new Range({ low: "A", high: "M" }), link: "link1" } + ]); + + assert.throws( + function () { + var result = resolver.resolveForCreate("X"); + }, + /Error: Invalid operation: A containing range for 'X,X' doesn't exist in the partition map./ + ); + done(); + }); + }); + }); });