Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -674,11 +674,19 @@ Datastore.prototype.isKey = Datastore.isKey = function(value) {
/**
* Create a new Transaction object.
*
* @param {object} [options] Configuration object.
* @param {string} [options.id] The ID of a previously run transaction.
* @param {boolean} [options.readOnly=false] A read-only transaction cannot
* modify entities.
* @returns {Transaction}
* @private
*
* @example
* const Datastore = require('@google-cloud/datastore');
* const datastore = new Datastore();
* const transaction = datastore.transaction();
*/
Datastore.prototype.transaction = function() {
return new Transaction(this);
Datastore.prototype.transaction = function(options) {
return new Transaction(this, options);
};

/**
Expand Down
45 changes: 37 additions & 8 deletions src/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ var Request = require('./request.js');
* const datastore = new Datastore();
* const transaction = datastore.transaction();
*/
function Transaction(datastore) {
function Transaction(datastore, options) {
/**
* @name Transaction#datastore
* @type {Datastore}
Expand All @@ -62,6 +62,11 @@ function Transaction(datastore) {
*/
this.namespace = datastore.namespace;

options = options || {};

this.id = options.id;
this.readOnly = options.readOnly === true;

this.request = datastore.request_.bind(datastore);

// A queue for entity modifications made during the transaction.
Expand Down Expand Up @@ -378,8 +383,12 @@ Transaction.prototype.rollback = function(gaxOptions, callback) {
* Begin a remote transaction. In the callback provided, run your transactional
* commands.
*
* @param {object} [gaxOptions] Request configuration options, outlined here:
* https://googleapis.github.io/gax-nodejs/global.html#CallOptions.
* @param {object} [options] Configuration object.
* @param {object} [options.gaxOptions] Request configuration options, outlined
* here: https://googleapis.github.io/gax-nodejs/global.html#CallOptions.
* @param {boolean} [options.readOnly=false] A read-only transaction cannot
* modify entities.
* @param {string} [options.transactionId] The ID of a previous transaction.
* @param {function} callback The function to execute within the context of
* a transaction.
* @param {?error} callback.err An error returned while making this request.
Expand Down Expand Up @@ -420,21 +429,41 @@ Transaction.prototype.rollback = function(gaxOptions, callback) {
* var apiResponse = data[1];
* });
*/
Transaction.prototype.run = function(gaxOptions, callback) {
Transaction.prototype.run = function(options, callback) {

This comment was marked as spam.

var self = this;

if (is.fn(gaxOptions)) {
callback = gaxOptions;
gaxOptions = {};
if (is.fn(options)) {
callback = options;
options = {};
}

options = options || {};
callback = callback || common.util.noop;

var reqOpts = {
transactionOptions: {}
};

if (options.readOnly || this.readOnly) {
reqOpts.transactionOptions.readOnly = {};
}

if (options.transactionId || this.id) {
reqOpts.transactionOptions.readWrite = {
previousTransaction: options.transactionId || this.id
};
}

if (options.transactionOptions) {
reqOpts.transactionOptions = options.transactionOptions;
}

this.request_(
{
client: 'DatastoreClient',
method: 'beginTransaction',
gaxOpts: gaxOptions,
reqOpts: reqOpts,
gaxOpts: options.gaxOptions,
},
function(err, resp) {
if (err) {
Expand Down
30 changes: 30 additions & 0 deletions system-test/datastore.js
Original file line number Diff line number Diff line change
Expand Up @@ -979,5 +979,35 @@ describe('Datastore', function() {
});
});
});

it('should read in a readOnly transaction', function(done) {
var transaction = datastore.transaction({ readOnly: true });
var key = datastore.key(['Company', 'Google']);

transaction.run(function(err) {
assert.ifError(err);
transaction.get(key, done);
});
});

it('should not write in a readOnly transaction', function(done) {
var transaction = datastore.transaction({ readOnly: true });
var key = datastore.key(['Company', 'Google']);

transaction.run(function(err) {
assert.ifError(err);

transaction.get(key, function(err) {
assert.ifError(err);

transaction.save({ key: key, data: {} });

transaction.commit(function(err) {
assert(err instanceof Error);
done();
});
});
});
});
});
});
6 changes: 6 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,12 @@ describe('Datastore', function() {
var transaction = datastore.transaction();
assert.strictEqual(transaction.calledWith_[0], datastore);
});

it('should pass options to the Transaction constructor', function() {
var options = {};
var transaction = datastore.transaction(options);
assert.strictEqual(transaction.calledWith_[1], options);
});
});

describe('determineBaseUrl_', function() {
Expand Down
100 changes: 98 additions & 2 deletions test/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,24 @@ describe('Transaction', function() {
assert.strictEqual(transaction.namespace, NAMESPACE);
});

it('should localize the transaction ID', function() {
var options = {
id: 'transaction-id'
};

var transaction = new Transaction(DATASTORE, options);
assert.strictEqual(transaction.id, options.id);
});

it('should localize readOnly', function() {
var options = {
readOnly: true
};

var transaction = new Transaction(DATASTORE, options);
assert.strictEqual(transaction.readOnly, true);
});

it('should localize request function', function(done) {
var transaction;

Expand Down Expand Up @@ -440,7 +458,8 @@ describe('Transaction', function() {
transaction.request_ = function(config) {
assert.strictEqual(config.client, 'DatastoreClient');
assert.strictEqual(config.method, 'beginTransaction');
assert.deepEqual(config.gaxOpts, {});
assert.deepEqual(config.reqOpts, { transactionOptions: {} });
assert.strictEqual(config.gaxOpts, undefined);
done();
};

Expand All @@ -455,7 +474,84 @@ describe('Transaction', function() {
done();
};

transaction.run(gaxOptions);
transaction.run({ gaxOptions: gaxOptions });
});

describe('options.readOnly', function() {
it('should respect the readOnly option', function(done) {
var options = {
readOnly: true
};

transaction.request_ = function(config) {
assert.deepEqual(config.reqOpts.transactionOptions.readOnly, {});
done();
};

transaction.run(options, assert.ifError);
});

it('should respect the global readOnly option', function(done) {
transaction.readOnly = true;

transaction.request_ = function(config) {
assert.deepEqual(config.reqOpts.transactionOptions.readOnly, {});
done();
};

transaction.run(assert.ifError);
});
});

describe('options.transactionId', function() {
it('should respect the transactionId option', function(done) {
var options = {
transactionId: 'transaction-id'
};

transaction.request_ = function(config) {
assert.deepEqual(config.reqOpts.transactionOptions.readWrite, {
previousTransaction: options.transactionId
});
done();
};

transaction.run(options, assert.ifError);
});

it('should respect the global transactionId option', function(done) {
transaction.id = 'transaction-id';

transaction.request_ = function(config) {
assert.deepEqual(config.reqOpts.transactionOptions.readWrite, {
previousTransaction: transaction.id
});
done();
};

transaction.run(assert.ifError);
});
});

describe('options.transactionOptions', function() {
it('should allow full override of transactionOptions', function(done) {
transaction.readOnly = true;

var options = {
transactionOptions: {
readWrite: {
previousTransaction: 'transaction-id'
}
}
};

transaction.request_ = function(config) {
assert.deepEqual(config.reqOpts, options);
done();
};

transaction.run(options, assert.ifError);
});
});

describe('error', function() {
Expand Down