Merge pull request #43 from shipunyc/master

Implements query conflicts and fixes bugs.
This commit is contained in:
shipunyc 2015-05-12 14:07:43 -07:00
Родитель c993a457ea 49d99e7d49
Коммит 0d6fb7f2e4
3 изменённых файлов: 192 добавлений и 103 удалений

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

@ -17,7 +17,7 @@ var DocumentClient = Base.defineClass(
* @constructor DocumentClient
* @param {string} urlConnection - The service endpoint to use to create the client.
* @param {object} auth - An object that is used for authenticating requests and must contains one of the options
* @param {string} [auth.masterKey] - The authorization master key to use to create the client.
* @param {string} [auth.masterkey] - The authorization master key to use to create the client.
* @param {Object} [auth.resourceTokens] - An object that contains resources tokens. Keys for the object are resource Ids and values are the resource tokens.
* @param {Array} [auth.permissionFeed] - An array of {@link Permission} objects.
* @param {object} [connectionPolicy] - An instance of {@link ConnectionPolicy} class. This parameter is optional and the default connectionPolicy will be used if omitted.
@ -34,7 +34,7 @@ var DocumentClient = Base.defineClass(
var resourceParts = auth.permissionFeed[i].resource.split("/");
var rid = resourceParts[resourceParts.length - 1];
this.resourceTokens[rid] = auth.permissionFeed[i]._token;
}
}
}
}
@ -117,24 +117,28 @@ var DocumentClient = Base.defineClass(
* </p>
* @memberof DocumentClient
* @instance
* @param {string} collectionLink - The self-link of the collection.
* @param {object} body - Represents the body of the document. Can contain any number of user defined properties.
* @param {string} [body.id] - The id of the document, MUST be unique for each document.
* @param {RequestOptions} [options] - The request options.
* @param {boolean} [options.disableAutomaticIdGeneration] - Disables the automatic id generation. If id is missing in the body and this option is true, an error will be returned.
* @param {RequestCallback} callback - The callback for the request.
* @param {string} collectionLink - The self-link of the collection.
* @param {object} body - Represents the body of the document. Can contain any number of user defined properties.
* @param {string} [body.id] - The id of the document, MUST be unique for each document.
* @param {RequestOptions} [options] - The request options.
* @param {boolean} [options.disableAutomaticIdGeneration] - Disables the automatic id generation. If id is missing in the body and this option is true, an error will be returned.
* @param {RequestCallback} callback - The callback for the request.
*/
createDocument: function (collectionLink, body, options, callback) {
if (!callback) {
callback = options;
options = {};
}
if (!options) {
options = {};
}
// 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 = Base.generateGuidId();
}
// 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 = Base.generateGuidId();
}
var path = "/" + collectionLink + "docs/";
var resourceInfo = Base.parsePath(collectionLink);
this.create(body, path, "docs", resourceInfo.objectBody.id, undefined, options, callback);
@ -220,11 +224,11 @@ var DocumentClient = Base.defineClass(
* @memberof DocumentClient
* @instance
* @param {string} collectionLink - The self-link of the collection.
* @param {object} trigger - The specification of the trigger.
* @param {string} trigger.id - The id of the trigger.
* @param {object} trigger - Represents the body of the trigger.
* @param {string} trigger.id - The id of the trigger.
* @param {string} trigger.triggerType - The type of the trigger, should be one of the values of {@link TriggerType}.
* @param {string} trigger.triggerOperation - The trigger operation, should be one of the values of {@link TriggerOperation}.
* @param {function} trigger.body - The body of the trigger, it can be passed as stringified too.
* @param {function} trigger.serverScript - The body of the trigger, it can be passed as stringified too.
* @param {RequestOptions} [options] - The request options.
* @param {RequestCallback} callback - The callback for the request.
*/
@ -256,10 +260,10 @@ var DocumentClient = Base.defineClass(
* @memberof DocumentClient
* @instance
* @param {string} collectionLink - The self-link of the collection.
* @param {object} udf - The specification of the userDefinedFunction.
* @param {string} udf.id - The id of the udf.
* @param {object} udf - Represents the body of the userDefinedFunction.
* @param {string} udf.id - The id of the udf.
* @param {string} udf.userDefinedFunctionType - The type of the udf, it should be one of the values of {@link UserDefinedFunctionType}
* @param {function} udf.body - Represents the body of the udf, it can be passed as stringified too.
* @param {function} udf.serverScript - Represents the body of the udf, it can be passed as stringified too.
* @param {RequestOptions} [options] - The request options.
* @param {RequestCallback} callback - The callback for the request.
*/
@ -290,9 +294,9 @@ var DocumentClient = Base.defineClass(
* @memberof DocumentClient
* @instance
* @param {string} collectionLink - The self-link of the collection.
* @param {object} sproc - The specification of the stored procedure.
* @param {string} sproc.id - The id of the stored procedure.
* @param {function} sproc.body - The body of the stored procedure, it can be passed as stringified too.
* @param {object} sproc - Represents the body of the stored procedure.
* @param {string} sproc.id - The id of the stored procedure.
* @param {function} sproc.serverScript - The body of the stored procedure, it can be passed as stringified too.
* @param {RequestOptions} [options] - The request options.
* @param {RequestCallback} callback - The callback for the request.
*/
@ -652,21 +656,7 @@ var DocumentClient = Base.defineClass(
* @returns {QueryIterator} - An instance of QueryIterator to handle reading feed.
*/
readConflicts: function (collectionLink, options) {
var that = this;
var path = "/" + collectionLink + "conflicts/";
var resourceInfo = Base.parsePath(collectionLink);
return new QueryIterator(this, "", options, function(options, callback){
that.queryFeed.call(that,
that,
path,
"conflicts",
resourceInfo.objectBody.id,
function(result) { return result.Conflicts; },
function(parent, body) { return body; },
"",
options,
callback);
});
return this.queryConflicts(collectionLink, undefined, options);
},
/** Lists all databases that satisfy a query.
@ -908,6 +898,33 @@ var DocumentClient = Base.defineClass(
});
},
/**
* Query the conflicts for the collection.
* @memberof DocumentClient
* @instance
* @param {string} collectionLink - The self-link of the collection.
* @param {SqlQuerySpec | string} query - A SQL query.
* @param {FeedOptions} [options] - Represents the feed options.
* @returns {QueryIterator} - An instance of queryIterator to handle reading feed.
*/
queryConflicts: function (collectionLink, query, options) {
var that = this;
var path = "/" + collectionLink + "conflicts/";
var resourceInfo = Base.parsePath(collectionLink);
return new QueryIterator(this, query, options, function(options, callback){
that.queryFeed.call(that,
that,
path,
"conflicts",
resourceInfo.objectBody.id,
function(result) { return result.Conflicts; },
function(parent, body) { return body; },
query,
options,
callback);
});
},
/**
* Delete the database object.
* @memberof DocumentClient
@ -1184,14 +1201,10 @@ var DocumentClient = Base.defineClass(
* Replace the trigger object.
* @memberof DocumentClient
* @instance
* @param {string} triggerLink - The self-link of the trigger.
* @param {object} trigger - The specification of the new trigger.
* @param {string} trigger.id - The id of the trigger.
* @param {string} trigger.triggerType - The type of the trigger, should be one of the values of {@link TriggerType}.
* @param {string} trigger.triggerOperation - The trigger operation, should be one of the values of {@link TriggerOperation}.
* @param {function} trigger.body - The body of the trigger, it can be passed as stringified too.
* @param {RequestOptions} [options] - The request options.
* @param {RequestCallback} callback - The callback for the request.
* @param {string} triggerLink - The self-link of the trigger.
* @param {object} trigger - Represent the new trigger body.
* @param {RequestOptions} [options] - The request options.
* @param {RequestCallback} callback - The callback for the request.
*/
replaceTrigger: function(triggerLink, trigger, options, callback) {
if (!callback) {
@ -1214,13 +1227,10 @@ var DocumentClient = Base.defineClass(
* Replace the UserDefinedFunction object.
* @memberof DocumentClient
* @instance
* @param {string} udfLink - The self-link of the user defined function.
* @param {object} udf - The specification of the new udf.
* @param {string} udf.id - The id of the udf.
* @param {string} udf.userDefinedFunctionType - The type of the udf, it should be one of the values of {@link UserDefinedFunctionType}
* @param {function} udf.body - Represents the body of the udf, it can be passed as stringified too.
* @param {RequestOptions} [options] - The request options.
* @param {RequestCallback} callback - The callback for the request.
* @param {string} udfLink - The self-link of the user defined function.
* @param {object} udf - Represent the new udf body.
* @param {RequestOptions} [options] - The request options.
* @param {RequestCallback} callback - The callback for the request.
*/
replaceUserDefinedFunction: function(udfLink, udf, options, callback) {
if (!callback) {
@ -1243,12 +1253,10 @@ var DocumentClient = Base.defineClass(
* Replace the StoredProcedure object.
* @memberof DocumentClient
* @instance
* @param {string} sprocLink - The self-link of the stored procedure.
* @param {object} sproc - The specification of the new stored procedure.
* @param {string} sproc.id - The id of the stored procedure.
* @param {function} sproc.body - The body of the stored procedure, it can be passed as stringified too.
* @param {RequestOptions} [options] - The request options.
* @param {RequestCallback} callback - The callback for the request.
* @param {string} sprocLink - The self-link of the stored procedure.
* @param {object} sproc - Represent the new sproc body.
* @param {RequestOptions} [options] - The request options.
* @param {RequestCallback} callback - The callback for the request.
*/
replaceStoredProcedure: function(sprocLink, sproc, options, callback) {
if (!callback) {
@ -1542,8 +1550,8 @@ var DocumentClient = Base.defineClass(
break;
}
var headers = Base.getHeaders(documentclient, initialHeaders, "post", path, id, type, options);
documentclient.post(urlConnection, path, query, headers, successCallback);
var headers = Base.getHeaders(documentclient, initialHeaders, "post", path, id, type, options);
documentclient.post(urlConnection, path, query, headers, successCallback);
}
}
}

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

@ -440,11 +440,11 @@ var DocumentClientWrapper = Base.defineClass(
* </p>
* @memberof DocumentClientWrapper
* @instance
* @param {string} collectionLink - The self-link of the collection.
* @param {object} body - Represents the body of the document. Can contain any number of user defined properties.
* @param {string} [body.id] - The id of the document, MUST be unique for each document.
* @param {RequestOptions} [options] - The request options.
* @param {boolean} [options.disableAutomaticIdGeneration] - Disables the automatic id generation. If id is missing in the body and this option is true, an error will be returned.
* @param {string} collectionLink - The self-link of the collection.
* @param {object} body - Represents the body of the document. Can contain any number of user defined properties.
* @param {string} [body.id] - The id of the document, MUST be unique for each document.
* @param {RequestOptions} [options] - The request options.
* @param {boolean} [options.disableAutomaticIdGeneration] - Disables the automatic id generation. If id is missing in the body and this option is true, an error will be returned.
* @Returns {Object} <p>A promise object for the request completion. <br>
The onFulfilled callback takes a parameter of type {@link ResourceResponse} and the OnError callback takes a parameter of type {@link ResponseError}</p>
*/
@ -642,7 +642,7 @@ var DocumentClientWrapper = Base.defineClass(
readStoredProcedureAsync: function (sprocLink, options) {
return readOperationPromise(this._innerDocumentclient, "readStoredProcedure", sprocLink, options);
},
/**
* Get all conflicts in this collection.
* @memberof DocumentClientWrapper
@ -651,10 +651,23 @@ var DocumentClientWrapper = Base.defineClass(
* @param {FeedOptions} [options] - The feed options
* @returns {QueryIterator} - An instance of queryIterator to handle reading feed.
*/
readConflicts: function (collectionLink, options) {
readConflicts: function (collectionLink, options) {
return new QueryIteratorWrapper(this._innerDocumentclient.readConflicts(collectionLink, options));
},
/**
* Query the conflicts for the collection.
* @memberof DocumentClientWrapper
* @instance
* @param {string} collectionLink - The self-link of the collection.
* @param {SqlQuerySpec | string} query - A SQL query.
* @param {FeedOptions} [options] - Represents the feed options.
* @returns {QueryIterator} - An instance of queryIterator to handle reading feed.
*/
queryConflicts: function (collectionLink, query, options) {
return new QueryIteratorWrapper(this._innerDocumentclient.queryConflicts(collectionLink, query, options));
},
/**
* Reads a conflict.
* @memberof DocumentClientWrapper
@ -664,7 +677,7 @@ var DocumentClientWrapper = Base.defineClass(
* @Returns {Object} <p>A promise object for the request completion. <br>
The onFulfilled callback takes a parameter of type {@link ResourceResponse} and the OnError callback takes a parameter of type {@link ResponseError}</p>
*/
readConflictAsync: function (conflictLink, options) {
readConflictAsync: function (conflictLink, options) {
return readOperationPromise(this._innerDocumentclient, "readConflict", conflictLink, options);
},
@ -1058,7 +1071,7 @@ var QueryIteratorWrapper = Base.defineClass(
var deferred = Q.defer();
var that = this;
this._innerQueryIterator.toArray(function(error, resources, responseHeaders) {
if (error) {
if (error) {
deferred.reject(error);
} else {
deferred.resolve({feed: resources, headers: responseHeaders});

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

@ -58,12 +58,10 @@ describe("NodeJS Client Q prmise Wrapper CRUD Tests", function(){
validateCreate: function(created) {
assert.equal(created.id, "sampleDb", "wrong id");
},
validateReplace: function(created, replaced) {
assert.equal(replaced.id, "replaced db", "id should change");
assert.equal(created.id, replaced.id, "id should stay the same");
validateReplace: function (created, replaced) {
// database doesn't support replace.
},
replaceProperties: function(resource) {
resource.id = "replaced db";
replaceProperties: function (resource) {
return resource;
}
};
@ -145,7 +143,7 @@ describe("NodeJS Client Q prmise Wrapper CRUD Tests", function(){
});
});
});
describe("Validate Attachment CRUD", function(){
var createReadableStream = function(firstChunk, secondChunk){
var readableStream = new Stream.Readable();
@ -772,18 +770,18 @@ describe("NodeJS Client Q prmise Wrapper CRUD Tests", function(){
createParentResourcesAsync(client, {db: true, coll: true})
.then(function(resources) {
var collection = resources.createdCollection;
var sproc1 = {
id: "storedProcedure1",
body: function () {
for (var i = 0; i < 1000; i++) {
var item = getContext().getResponse().getBody();
if (i > 0 && item != i - 1) throw 'body mismatch';
getContext().getResponse().setBody(i);
}
}
};
var sproc1 = {
id: "storedProcedure1",
body: function () {
for (var i = 0; i < 1000; i++) {
var item = getContext().getResponse().getBody();
if (i > 0 && item != i - 1) throw 'body mismatch';
getContext().getResponse().setBody(i);
}
}
};
client.createStoredProcedureAsync(collection._self, sproc1)
client.createStoredProcedureAsync(collection._self, sproc1)
.then(function (response) {
return client.executeStoredProcedureAsync(response.resource._self);
})
@ -832,6 +830,76 @@ describe("NodeJS Client Q prmise Wrapper CRUD Tests", function(){
});
});
});
describe("Validate Offer CRUD", function () {
it("[promiseApi] Should do offer CRUD operations successfully", function (done) {
var client = new DocumentDBClient(host, { masterKey: masterKey });
var existingOffer;
createParentResourcesAsync(client, { db: true, coll: true })
.then(function (createdResources) {
return client.readOffers().toArrayAsync();
}).then(function (result) {
var offers = result.feed;
assert.equal(offers.length, 1);
existingOffer = offers[0];
assert.equal(existingOffer.offerType, "S1"); // S1 is the default type
return client.readOfferAsync(existingOffer._self);
}).then(function (response) {
var readOffer = response.resource;
assert.equal(readOffer.id, existingOffer.id);
assert.equal(readOffer._rid, existingOffer._rid);
assert.equal(readOffer._self, existingOffer._self);
assert.equal(readOffer.offerType, existingOffer.offerType);
// Replace offer.
readOffer.offerType = "S2";
return client.replaceOfferAsync(readOffer._self, readOffer);
}).then(function (response) {
var replacedOffer = response.resource;
assert.equal(replacedOffer.offerType, "S2");
// Query for offer.
var querySpec = {
query: 'select * FROM root r WHERE r.id=@id',
parameters: [
{
name: '@id',
value: existingOffer.id
}
]
};
return client.queryOffers(querySpec).toArrayAsync();
}).then(function (result) {
var offers = result.feed;
assert.equal(offers.length, 1);
var oneOffer = offers[0];
assert.equal(oneOffer.offerType, "S2");
done();
}).fail(function (error) {
console.log(error, error.stack);
done();
});
});
it("[promiseApi] Should create Collection with specified offer type successfully", function (done) {
var client = new DocumentDBClient(host, { masterKey: masterKey });
client.createDatabaseAsync({ id: "sample database" })
.then(function (response) {
var db = response.resource;
return client.createCollectionAsync(db._self, { id: "sample coll" }, { offerType: "S2" });
}).then(function (response) {
return client.readOffers().toArrayAsync();
}).then(function (result) {
var offers = result.feed;
assert.equal(offers.length, 1);
var existingOffer = offers[0];
assert.equal(existingOffer.offerType, "S2"); // S2 is what we created.
done();
}).fail(function (error) {
console.log(error, error.stack);
done();
});
});
});
function validateCRUDAsync(client, parentLink, options) {
var deferred = Q.defer();
@ -854,10 +922,11 @@ describe("NodeJS Client Q prmise Wrapper CRUD Tests", function(){
validateCreate(createdResource);
return client["read" + className + "s"](parentLink).toArrayAsync();
})
.then(function(response) {
.then(function(response) {
var resources = response.feed;
assert(resources.length > 0, "number of resources for the query should be > 0");
var querySpec = {
if (parentLink) {
var querySpec = {
query: 'select * FROM root r WHERE r.id=@id',
parameters: [
{
@ -866,20 +935,19 @@ describe("NodeJS Client Q prmise Wrapper CRUD Tests", function(){
}
]
};
if (parentLink) {
return client["query" + className + "s"](parentLink, querySpec).toArrayAsync();
} else {
return client["query" + className + "s"](querySpec).toArrayAsync();
}
return client["query" + className + "s"](parentLink, querySpec).toArrayAsync();
} else {
return client["query" + className + "s"](querySpec).toArrayAsync();
}
})
.then(function(response) {
var resources = response.feed;
assert(resources.length > 0, "number of resources for the query should be > 0");
createdResource = replaceProperties(createdResource);
if (className !== "Collection") {
return client["replace" + className + "Async"](createdResource._self, createdResource);
if (className == "Collection" || className == "Database") {
return client["read" + className + "Async"](createdResource._self);
} else {
return client["read" + className + "Async"](createdResource._self);
return client["replace" + className + "Async"](createdResource._self, createdResource);
}
})
.then(function(response) {
@ -897,7 +965,7 @@ describe("NodeJS Client Q prmise Wrapper CRUD Tests", function(){
.then(function(response) {
assert.fail("", "", "request should return an error");
},
function(error){
function(error){
var notFoundErrorCode = 404;
assert.equal(error.code, notFoundErrorCode, "response should return error code 404");
deferred.resolve();
@ -930,11 +998,11 @@ describe("NodeJS Client Q prmise Wrapper CRUD Tests", function(){
deferred.resolve(createdResources);
})
} else if (options.user) {
client.createUserAsync(db._self, {id: "sample user"})
.then(function(response){
var user = createdResources.createdUser = response.resource;
deferred.resolve(createdResources);
});
client.createUserAsync(db._self, {id: "sample user"})
.then(function(response){
var user = createdResources.createdUser = response.resource;
deferred.resolve(createdResources);
});
} else {
deferred.resolve(createdResources);
}