Initial commit

This commit is contained in:
Nicolas Dextraze
2016-03-09 12:46:15 -08:00
parent 3a5a4111c1
commit 9be67bf7c7
54 changed files with 5172 additions and 1 deletions

View File

@ -0,0 +1,80 @@
var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand');
var InspectionDecision = require('../systemData/inspectionDecision');
var InspectionResult = require('./../systemData/inspectionResult');
var ClientMessage = require('../messages/clientMessage');
var WriteResult = require('../results').WriteResult;
var Position = require('../results').Position;
var OperationBase = require('../clientOperations/operationBase');
function AppendToStreamOperation(log, cb, requireMaster, stream, expectedVersion, events, userCredentials) {
OperationBase.call(this, log, cb, TcpCommand.WriteEvents, TcpCommand.WriteEventsCompleted, userCredentials);
this._responseType = ClientMessage.WriteEventsCompleted;
this._requireMaster = requireMaster;
this._stream = stream;
this._expectedVersion = expectedVersion;
this._events = events;
}
util.inherits(AppendToStreamOperation, OperationBase);
AppendToStreamOperation.prototype._createRequestDto = function() {
var dtos = this._events.map(function(ev) {
var eventId = new Buffer(uuid.parse(ev.eventId));
return new ClientMessage.NewEvent({
event_id: eventId, event_type: ev.type,
data_content_type: ev.isJson ? 1 : 0, metadata_content_type: 0,
data: ev.data, metadata: ev.metadata});
});
return new ClientMessage.WriteEvents({
event_stream_id: this._stream,
expected_version: this._expectedVersion,
events: dtos,
require_master: this._requireMaster});
};
AppendToStreamOperation.prototype._inspectResponse = function(response) {
switch (response.result)
{
case ClientMessage.OperationResult.Success:
if (this._wasCommitTimeout)
this.log.debug("IDEMPOTENT WRITE SUCCEEDED FOR %s.", this);
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "Success");
case ClientMessage.OperationResult.PrepareTimeout:
return new InspectionResult(InspectionDecision.Retry, "PrepareTimeout");
case ClientMessage.OperationResult.ForwardTimeout:
return new InspectionResult(InspectionDecision.Retry, "ForwardTimeout");
case ClientMessage.OperationResult.CommitTimeout:
this._wasCommitTimeout = true;
return new InspectionResult(InspectionDecision.Retry, "CommitTimeout");
case ClientMessage.OperationResult.WrongExpectedVersion:
var err = ["Append failed due to WrongExpectedVersion. Stream: ", this._stream,", Expected version: ", this._expectedVersion].join('');
this.fail(new Error(err));
return new InspectionResult(InspectionDecision.EndOperation, "WrongExpectedVersion");
case ClientMessage.OperationResult.StreamDeleted:
this.fail(new Error("Stream deleted. Stream: " + this._stream));
return new InspectionResult(InspectionDecision.EndOperation, "StreamDeleted");
case ClientMessage.OperationResult.InvalidTransaction:
this.fail(new Error("Invalid transaction."));
return new InspectionResult(InspectionDecision.EndOperation, "InvalidTransaction");
case ClientMessage.OperationResult.AccessDenied:
this.fail(new Error(["Write access denied for stream '", this._stream, "'."].join('')));
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
default:
throw new Error("Unexpected OperationResult: " + response.result);
}
};
AppendToStreamOperation.prototype._transformResponse = function(response) {
return new WriteResult(response.last_event_number, new Position(response.prepare_position || -1, response.commit_position || -1));
};
AppendToStreamOperation.prototype.toString = function() {
return util.format("Stream: %s, ExpectedVersion: %d", this._stream, this._expectedVersion);
};
module.exports = AppendToStreamOperation;

View File

@ -0,0 +1,65 @@
var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand');
var InspectionDecision = require('../systemData/inspectionDecision');
var InspectionResult = require('./../systemData/inspectionResult');
var ClientMessage = require('../messages/clientMessage');
var results = require('../results');
var OperationBase = require('../clientOperations/operationBase');
function CommitTransactionOperation(log, cb, requireMaster, transactionId, userCredentials) {
OperationBase.call(this, log, cb, TcpCommand.TransactionCommit, TcpCommand.TransactionCommitCompleted, userCredentials);
this._responseType = ClientMessage.TransactionCommitCompleted;
this._requireMaster = requireMaster;
this._transactionId = transactionId;
}
util.inherits(CommitTransactionOperation, OperationBase);
CommitTransactionOperation.prototype._createRequestDto = function() {
return new ClientMessage.TransactionCommit(this._transactionId, this._requireMaster);
};
CommitTransactionOperation.prototype._inspectResponse = function(response) {
switch (response.result)
{
case ClientMessage.OperationResult.Success:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "Success");
case ClientMessage.OperationResult.PrepareTimeout:
return new InspectionResult(InspectionDecision.Retry, "PrepareTimeout");
case ClientMessage.OperationResult.CommitTimeout:
return new InspectionResult(InspectionDecision.Retry, "CommitTimeout");
case ClientMessage.OperationResult.ForwardTimeout:
return new InspectionResult(InspectionDecision.Retry, "ForwardTimeout");
case ClientMessage.OperationResult.WrongExpectedVersion:
var err = util.format("Commit transaction failed due to WrongExpectedVersion. TransactionID: %d.", this._transactionId);
this.fail(new Error(err));
return new InspectionResult(InspectionDecision.EndOperation, "WrongExpectedVersion");
case ClientMessage.OperationResult.StreamDeleted:
this.fail(new Error("Stream deleted."));
return new InspectionResult(InspectionDecision.EndOperation, "StreamDeleted");
case ClientMessage.OperationResult.InvalidTransaction:
this.fail(new Error("Invalid transaction."));
return new InspectionResult(InspectionDecision.EndOperation, "InvalidTransaction");
case ClientMessage.OperationResult.AccessDenied:
this.fail(new Error("Write access denied."));
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
default:
throw new Error(util.format("Unexpected OperationResult: %s.", response.result));
}
};
CommitTransactionOperation.prototype._transformResponse = function(response) {
var logPosition = new results.Position(response.prepare_position || -1, response.commit_position || -1);
return new results.WriteResult(response.last_event_number, logPosition);
};
CommitTransactionOperation.prototype.toString = function() {
return util.format("TransactionId: %s", this._transactionId);
};
module.exports = CommitTransactionOperation;

View File

@ -0,0 +1,66 @@
var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand');
var InspectionDecision = require('../systemData/inspectionDecision');
var InspectionResult = require('./../systemData/inspectionResult');
var ClientMessage = require('../messages/clientMessage');
var results = require('../results');
var OperationBase = require('../clientOperations/operationBase');
function DeleteStreamOperation(log, cb, requireMaster, stream, expectedVersion, hardDelete, userCredentials) {
OperationBase.call(this, log, cb, TcpCommand.DeleteStream, TcpCommand.DeleteStreamCompleted, userCredentials);
this._responseType = ClientMessage.DeleteStreamCompleted;
this._requireMaster = requireMaster;
this._stream = stream;
this._expectedVersion = expectedVersion;
this._hardDelete = hardDelete;
}
util.inherits(DeleteStreamOperation, OperationBase);
DeleteStreamOperation.prototype._createRequestDto = function() {
return new ClientMessage.DeleteStream(this._stream, this._expectedVersion, this._requireMaster, this._hardDelete);
};
DeleteStreamOperation.prototype._inspectResponse = function(response) {
switch (response.result)
{
case ClientMessage.OperationResult.Success:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "Success");
case ClientMessage.OperationResult.PrepareTimeout:
return new InspectionResult(InspectionDecision.Retry, "PrepareTimeout");
case ClientMessage.OperationResult.CommitTimeout:
return new InspectionResult(InspectionDecision.Retry, "CommitTimeout");
case ClientMessage.OperationResult.ForwardTimeout:
return new InspectionResult(InspectionDecision.Retry, "ForwardTimeout");
case ClientMessage.OperationResult.WrongExpectedVersion:
var err = util.format("Delete stream failed due to WrongExpectedVersion. Stream: %s, Expected version: %d.", this._stream, this._expectedVersion);
this.fail(new Error("Wrong expected version: " + err));
return new InspectionResult(InspectionDecision.EndOperation, "WrongExpectedVersion");
case ClientMessage.OperationResult.StreamDeleted:
this.fail(new Error("Stream deleted: " + this._stream));
return new InspectionResult(InspectionDecision.EndOperation, "StreamDeleted");
case ClientMessage.OperationResult.InvalidTransaction:
this.fail(new Error("Invalid transaction."));
return new InspectionResult(InspectionDecision.EndOperation, "InvalidTransaction");
case ClientMessage.OperationResult.AccessDenied:
this.fail(new Error(util.format("Write access denied for stream '%s'.", this._stream)));
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
default:
throw new Error(util.format("Unexpected OperationResult: %d.", response.result));
}
};
DeleteStreamOperation.prototype._transformResponse = function(response) {
return new results.DeleteResult(new results.Position(response.prepare_position || -1, response.commit_position || -1));
};
DeleteStreamOperation.prototype.toString = function() {
return util.format("Stream: %s, ExpectedVersion: %s.", this._stream, this._expectedVersion);
};
module.exports = DeleteStreamOperation;

View File

@ -0,0 +1,148 @@
var util = require('util');
var TcpPackage = require('../systemData/tcpPackage');
var TcpCommand = require('../systemData/tcpCommand');
var TcpFlags = require('../systemData/tcpFlags');
var InspectionDecision = require('../systemData/inspectionDecision');
var ClientMessage = require('../messages/clientMessage');
var createInspectionResult = require('./../systemData/inspectionResult');
var createBufferSegment = require('../common/bufferSegment');
function OperationBase(log, cb, requestCommand, responseCommand, userCredentials) {
this.log = log;
this._cb = cb;
this._requestCommand = requestCommand;
this._responseCommand = responseCommand;
this.userCredentials = userCredentials;
this._completed = false;
this._response = null;
this._responseType = null;
}
OperationBase.prototype._createRequestDto = function() {
throw new Error('_createRequestDto not implemented.');
};
OperationBase.prototype._inspectResponse = function() {
throw new Error('_inspectResponse not implemented.');
};
OperationBase.prototype._transformResponse = function() {
throw new Error('_transformResponse not implemented.');
};
OperationBase.prototype.fail = function(error) {
this._completed = true;
this._cb(error);
};
OperationBase.prototype._succeed = function() {
if (!this._completed) {
this._completed = true;
if (this._response != null)
this._cb(null, this._transformResponse(this._response));
else
this._cb(new Error("No result."))
}
};
OperationBase.prototype.createNetworkPackage = function(correlationId) {
var dto = this._createRequestDto();
var buf = dto.encode().toBuffer();
return new TcpPackage(
this._requestCommand,
this.userCredentials ? TcpFlags.Authenticated : TcpFlags.None,
correlationId,
this.userCredentials ? this.userCredentials.username : null,
this.userCredentials ? this.userCredentials.password : null,
createBufferSegment(buf));
};
OperationBase.prototype.inspectPackage = function(pkg) {
try {
if (pkg.command === this._responseCommand) {
this._response = this._responseType.decode(pkg.data.toBuffer());
return this._inspectResponse(this._response);
}
switch (pkg.command) {
case TcpCommand.NotAuthenticated:
return this._inspectNotAuthenticated(pkg);
case TcpCommand.BadRequest:
return this._inspectBadRequest(pkg);
case TcpCommand.NotHandled:
return this._inspectNotHandled(pkg);
default:
return this._inspectUnexpectedCommand(package, this._responseCommand);
}
} catch(e) {
this.fail(e);
return createInspectionResult(InspectionDecision.EndOperation, "Error - " + e.message);
}
};
OperationBase.prototype._inspectNotAuthenticated = function(pkg)
{
var message = '';
try {
message = pkg.data.toString();
} catch(e) {}
//TODO typed error
this.fail(new Error("Authentication error: " + message));
return createInspectionResult(InspectionDecision.EndOperation, "NotAuthenticated");
};
OperationBase.prototype._inspectBadRequest = function(pkg)
{
var message = '';
try {
message = pkg.data.toString();
} catch(e) {}
//TODO typed error
this.fail(new Error("Bad request: " + message));
return createInspectionResult(InspectionDecision.EndOperation, "BadRequest - " + message);
};
OperationBase.prototype._inspectNotHandled = function(pkg)
{
var message = ClientMessage.NotHandled.decode(pkg.data.toBuffer());
switch (message.reason)
{
case ClientMessage.NotHandled.NotHandledReason.NotReady:
return createInspectionResult(InspectionDecision.Retry, "NotHandled - NotReady");
case ClientMessage.NotHandled.NotHandledReason.TooBusy:
return createInspectionResult(InspectionDecision.Retry, "NotHandled - TooBusy");
case ClientMessage.NotHandled.NotHandledReason.NotMaster:
var masterInfo = ClientMessage.NotHandled.MasterInfo.decode(message.additional_info);
return new InspectionResult(InspectionDecision.Reconnect, "NotHandled - NotMaster",
{host: masterInfo.external_tcp_address, port: masterInfo.external_tcp_port},
{host: masterInfo.external_secure_tcp_address, port: masterInfo.external_secure_tcp_port});
default:
this.log.error("Unknown NotHandledReason: %s.", message.reason);
return createInspectionResult(InspectionDecision.Retry, "NotHandled - <unknown>");
}
};
OperationBase.prototype._inspectUnexpectedCommand = function(pkg, expectedCommand)
{
if (pkg.command == expectedCommand)
throw new Error("Command shouldn't be " + TcpCommand.getName(pkg.command));
this.log.error("Unexpected TcpCommand received.\n"
+ "Expected: %s, Actual: %s, Flags: %s, CorrelationId: %s\n"
+ "Operation (%s): %s\n"
+"TcpPackage Data Dump:\n%j",
expectedCommand, TcpCommand.getName(pkg.command), pkg.flags, pkg.correlationId,
this.constructor.name, this, pkg.data);
this.fail(new Error(util.format("Unexpected command. Expecting %s got %s.", TcpCommand.getName(expectedCommand), TcpCommand.getName(pkg.command))));
return createInspectionResult(InspectionDecision.EndOperation, "Unexpected command - " + TcpCommand.getName(pkg.command));
};
module.exports = OperationBase;

View File

@ -0,0 +1,61 @@
var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand');
var ClientMessage = require('../messages/clientMessage');
var ReadDirection = require('../readDirection');
var InspectionResult = require('./../systemData/inspectionResult');
var InspectionDecision = require('../systemData/inspectionDecision');
var results = require('../results');
var OperationBase = require('./operationBase');
function ReadAllEventsBackwardOperation(
log, cb, position, maxCount, resolveLinkTos, requireMaster, userCredentials
) {
OperationBase.call(this, log, cb, TcpCommand.ReadAllEventsBackward, TcpCommand.ReadAllEventsBackwardCompleted, userCredentials);
this._responseType = ClientMessage.ReadAllEventsCompleted;
this._position = position;
this._maxCount = maxCount;
this._resolveLinkTos = resolveLinkTos;
this._requireMaster = requireMaster;
}
util.inherits(ReadAllEventsBackwardOperation, OperationBase);
ReadAllEventsBackwardOperation.prototype._createRequestDto = function() {
return new ClientMessage.ReadAllEvents(this._position.commitPosition, this._position.preparePosition, this._maxCount, this._resolveLinkTos, this._requireMaster);
};
ReadAllEventsBackwardOperation.prototype._inspectResponse = function(response) {
switch (response.result)
{
case ClientMessage.ReadAllEventsCompleted.ReadAllResult.Success:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "Success");
case ClientMessage.ReadAllEventsCompleted.ReadAllResult.Error:
this.fail(new Error("Server error: " + response.error));
return new InspectionResult(InspectionDecision.EndOperation, "Error");
case ClientMessage.ReadAllEventsCompleted.ReadAllResult.AccessDenied:
this.fail(new Error("Read access denied for $all."));
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
default:
throw new Error(util.format("Unexpected ReadStreamResult: %s.", response.result));
}
};
ReadAllEventsBackwardOperation.prototype._transformResponse = function(response) {
return new results.AllEventsSlice(
ReadDirection.Backward,
new results.Position(response.commit_position, response.prepare_position),
new results.Position(response.next_commit_position, response.next_prepare_position),
response.events
)
};
ReadAllEventsBackwardOperation.prototype.toString = function() {
return util.format("Position: %j, MaxCount: %d, ResolveLinkTos: %s, RequireMaster: %s",
this._position, this._maxCount, this._resolveLinkTos, this._requireMaster);
};
module.exports = ReadAllEventsBackwardOperation;

View File

@ -0,0 +1,61 @@
var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand');
var ClientMessage = require('../messages/clientMessage');
var ReadDirection = require('../readDirection');
var InspectionResult = require('./../systemData/inspectionResult');
var InspectionDecision = require('../systemData/inspectionDecision');
var results = require('../results');
var OperationBase = require('./operationBase');
function ReadAllEventsForwardOperation(
log, cb, position, maxCount, resolveLinkTos, requireMaster, userCredentials
) {
OperationBase.call(this, log, cb, TcpCommand.ReadAllEventsForward, TcpCommand.ReadAllEventsForwardCompleted, userCredentials);
this._responseType = ClientMessage.ReadAllEventsCompleted;
this._position = position;
this._maxCount = maxCount;
this._resolveLinkTos = resolveLinkTos;
this._requireMaster = requireMaster;
}
util.inherits(ReadAllEventsForwardOperation, OperationBase);
ReadAllEventsForwardOperation.prototype._createRequestDto = function() {
return new ClientMessage.ReadAllEvents(this._position.commitPosition, this._position.preparePosition, this._maxCount, this._resolveLinkTos, this._requireMaster);
};
ReadAllEventsForwardOperation.prototype._inspectResponse = function(response) {
switch (response.result)
{
case ClientMessage.ReadAllEventsCompleted.ReadAllResult.Success:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "Success");
case ClientMessage.ReadAllEventsCompleted.ReadAllResult.Error:
this.fail(new Error("Server error: " + response.error));
return new InspectionResult(InspectionDecision.EndOperation, "Error");
case ClientMessage.ReadAllEventsCompleted.ReadAllResult.AccessDenied:
this.fail(new Error("Read access denied for $all."));
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
default:
throw new Error(util.format("Unexpected ReadStreamResult: %s.", response.result));
}
};
ReadAllEventsForwardOperation.prototype._transformResponse = function(response) {
return new results.AllEventsSlice(
ReadDirection.Forward,
new results.Position(response.commit_position, response.prepare_position),
new results.Position(response.next_commit_position, response.next_prepare_position),
response.events
)
};
ReadAllEventsForwardOperation.prototype.toString = function() {
return util.format("Position: %j, MaxCount: %d, ResolveLinkTos: %s, RequireMaster: %s",
this._position, this._maxCount, this._resolveLinkTos, this._requireMaster);
};
module.exports = ReadAllEventsForwardOperation;

View File

@ -0,0 +1,79 @@
var util = require('util');
var TcpCommand = require('../systemData/tcpCommand');
var ClientMessage = require('../messages/clientMessage');
var InspectionResult = require('./../systemData/inspectionResult');
var InspectionDecision = require('../systemData/inspectionDecision');
var results = require('../results');
var OperationBase = require('./operationBase');
function ReadEventOperation(log, cb, stream, eventNumber, resolveLinkTos, requireMaster, userCredentials) {
OperationBase.call(this, log, cb, TcpCommand.ReadEvent, TcpCommand.ReadEventCompleted, userCredentials);
this._responseType = ClientMessage.ReadEventCompleted;
this._stream = stream;
this._eventNumber = eventNumber;
this._resolveLinkTos = resolveLinkTos;
this._requireMaster = requireMaster;
}
util.inherits(ReadEventOperation, OperationBase);
ReadEventOperation.prototype._createRequestDto = function() {
return new ClientMessage.ReadEvent(this._stream, this._eventNumber, this._resolveLinkTos, this._requireMaster);
};
ReadEventOperation.prototype._inspectResponse = function(response) {
switch (response.result)
{
case ClientMessage.ReadEventCompleted.ReadEventResult.Success:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "Success");
case ClientMessage.ReadEventCompleted.ReadEventResult.NotFound:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "NotFound");
case ClientMessage.ReadEventCompleted.ReadEventResult.NoStream:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "NoStream");
case ClientMessage.ReadEventCompleted.ReadEventResult.StreamDeleted:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "StreamDeleted");
case ClientMessage.ReadEventCompleted.ReadEventResult.Error:
this.fail(new Error("Server error: " + response.error));
return new InspectionResult(InspectionDecision.EndOperation, "Error");
case ClientMessage.ReadEventCompleted.ReadEventResult.AccessDenied:
this.fail(new Error(util.format("Read access denied for stream '%s'.", this._stream)));
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
default:
throw new Error(util.format("Unexpected ReadEventResult: %s.", response.result));
}
};
ReadEventOperation.prototype._transformResponse = function(response) {
return new results.EventReadResult(convert(response.result), this._stream, this._eventNumber, response.event);
};
function convert(result)
{
switch (result)
{
case ClientMessage.ReadEventCompleted.ReadEventResult.Success:
return results.EventReadStatus.Success;
case ClientMessage.ReadEventCompleted.ReadEventResult.NotFound:
return results.EventReadStatus.NotFound;
case ClientMessage.ReadEventCompleted.ReadEventResult.NoStream:
return results.EventReadStatus.NoStream;
case ClientMessage.ReadEventCompleted.ReadEventResult.StreamDeleted:
return results.EventReadStatus.StreamDeleted;
default:
throw new Error(util.format("Unexpected ReadEventResult: %s.", result));
}
}
ReadEventOperation.prototype.toString = function() {
return util.format("Stream: %s, EventNumber: %s, ResolveLinkTo: %s, RequireMaster: %s",
this._stream, this._eventNumber, this._resolveLinkTos, this._requireMaster);
};
module.exports = ReadEventOperation;

View File

@ -0,0 +1,73 @@
var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand');
var ClientMessage = require('../messages/clientMessage');
var ReadDirection = require('../readDirection');
var StatusCode = require('../systemData/statusCode');
var InspectionResult = require('./../systemData/inspectionResult');
var InspectionDecision = require('../systemData/inspectionDecision');
var results = require('../results');
var OperationBase = require('./operationBase');
function ReadStreamEventsBackwardOperation(
log, cb, stream, fromEventNumber, maxCount, resolveLinkTos, requireMaster, userCredentials
) {
OperationBase.call(this, log, cb, TcpCommand.ReadStreamEventsBackward, TcpCommand.ReadStreamEventsBackwardCompleted, userCredentials);
this._responseType = ClientMessage.ReadStreamEventsCompleted;
this._stream = stream;
this._fromEventNumber = fromEventNumber;
this._maxCount = maxCount;
this._resolveLinkTos = resolveLinkTos;
this._requireMaster = requireMaster;
}
util.inherits(ReadStreamEventsBackwardOperation, OperationBase);
ReadStreamEventsBackwardOperation.prototype._createRequestDto = function() {
return new ClientMessage.ReadStreamEvents(this._stream, this._fromEventNumber, this._maxCount, this._resolveLinkTos, this._requireMaster);
};
ReadStreamEventsBackwardOperation.prototype._inspectResponse = function(response) {
switch (response.result)
{
case ClientMessage.ReadStreamEventsCompleted.ReadStreamResult.Success:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "Success");
case ClientMessage.ReadStreamEventsCompleted.ReadStreamResult.StreamDeleted:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "StreamDeleted");
case ClientMessage.ReadStreamEventsCompleted.ReadStreamResult.NoStream:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "NoStream");
case ClientMessage.ReadStreamEventsCompleted.ReadStreamResult.Error:
this.fail(new Error("Server error: " + response.error));
return new InspectionResult(InspectionDecision.EndOperation, "Error");
case ClientMessage.ReadStreamEventsCompleted.ReadStreamResult.AccessDenied:
this.fail(new Error(util.format("Read access denied for stream '%s'.", this._stream)));
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
default:
throw new Error(util.format("Unexpected ReadStreamResult: %s.", response.result));
}
};
ReadStreamEventsBackwardOperation.prototype._transformResponse = function(response) {
return new results.StreamEventsSlice(
StatusCode.convert(response.result),
this._stream,
this._fromEventNumber,
ReadDirection.Backward,
response.events,
response.next_event_number,
response.last_event_number,
response.is_end_of_stream
)
};
ReadStreamEventsBackwardOperation.prototype.toString = function() {
return util.format("Stream: %s, FromEventNumber: %d, MaxCount: %d, ResolveLinkTos: %s, RequireMaster: %s",
this._stream, this._fromEventNumber, this._maxCount, this._resolveLinkTos, this._requireMaster);
};
module.exports = ReadStreamEventsBackwardOperation;

View File

@ -0,0 +1,73 @@
var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand');
var ClientMessage = require('../messages/clientMessage');
var ReadDirection = require('../readDirection');
var StatusCode = require('../systemData/statusCode');
var InspectionResult = require('./../systemData/inspectionResult');
var InspectionDecision = require('../systemData/inspectionDecision');
var results = require('../results');
var OperationBase = require('./operationBase');
function ReadStreamEventsForwardOperation(
log, cb, stream, fromEventNumber, maxCount, resolveLinkTos, requireMaster, userCredentials
) {
OperationBase.call(this, log, cb, TcpCommand.ReadStreamEventsForward, TcpCommand.ReadStreamEventsForwardCompleted, userCredentials);
this._responseType = ClientMessage.ReadStreamEventsCompleted;
this._stream = stream;
this._fromEventNumber = fromEventNumber;
this._maxCount = maxCount;
this._resolveLinkTos = resolveLinkTos;
this._requireMaster = requireMaster;
}
util.inherits(ReadStreamEventsForwardOperation, OperationBase);
ReadStreamEventsForwardOperation.prototype._createRequestDto = function() {
return new ClientMessage.ReadStreamEvents(this._stream, this._fromEventNumber, this._maxCount, this._resolveLinkTos, this._requireMaster);
};
ReadStreamEventsForwardOperation.prototype._inspectResponse = function(response) {
switch (response.result)
{
case ClientMessage.ReadStreamEventsCompleted.ReadStreamResult.Success:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "Success");
case ClientMessage.ReadStreamEventsCompleted.ReadStreamResult.StreamDeleted:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "StreamDeleted");
case ClientMessage.ReadStreamEventsCompleted.ReadStreamResult.NoStream:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "NoStream");
case ClientMessage.ReadStreamEventsCompleted.ReadStreamResult.Error:
this.fail(new Error("Server error: " + response.error));
return new InspectionResult(InspectionDecision.EndOperation, "Error");
case ClientMessage.ReadStreamEventsCompleted.ReadStreamResult.AccessDenied:
this.fail(new Error(util.format("Read access denied for stream '%s'.", this._stream)));
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
default:
throw new Error(util.format("Unexpected ReadStreamResult: %s.", response.result));
}
};
ReadStreamEventsForwardOperation.prototype._transformResponse = function(response) {
return new results.StreamEventsSlice(
StatusCode.convert(response.result),
this._stream,
this._fromEventNumber,
ReadDirection.Forward,
response.events,
response.next_event_number,
response.last_event_number,
response.is_end_of_stream
)
};
ReadStreamEventsForwardOperation.prototype.toString = function() {
return util.format("Stream: %s, FromEventNumber: %d, MaxCount: %d, ResolveLinkTos: %s, RequireMaster: %s",
this._stream, this._fromEventNumber, this._maxCount, this._resolveLinkTos, this._requireMaster);
};
module.exports = ReadStreamEventsForwardOperation;

View File

@ -0,0 +1,66 @@
var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand');
var InspectionDecision = require('../systemData/inspectionDecision');
var InspectionResult = require('./../systemData/inspectionResult');
var ClientMessage = require('../messages/clientMessage');
var EventStoreTransaction = require('../eventStoreTransaction');
var results = require('../results');
var OperationBase = require('../clientOperations/operationBase');
function StartTransactionOperation(log, cb, requireMaster, stream, expectedVersion, parentConnection, userCredentials) {
OperationBase.call(this, log, cb, TcpCommand.TransactionStart, TcpCommand.TransactionStartCompleted, userCredentials);
this._responseType = ClientMessage.TransactionStartCompleted;
this._requireMaster = requireMaster;
this._stream = stream;
this._expectedVersion = expectedVersion;
this._parentConnection = parentConnection;
}
util.inherits(StartTransactionOperation, OperationBase);
StartTransactionOperation.prototype._createRequestDto = function() {
return new ClientMessage.TransactionStart(this._stream, this._expectedVersion, this._requireMaster);
};
StartTransactionOperation.prototype._inspectResponse = function(response) {
switch (response.result)
{
case ClientMessage.OperationResult.Success:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "Success");
case ClientMessage.OperationResult.PrepareTimeout:
return new InspectionResult(InspectionDecision.Retry, "PrepareTimeout");
case ClientMessage.OperationResult.CommitTimeout:
return new InspectionResult(InspectionDecision.Retry, "CommitTimeout");
case ClientMessage.OperationResult.ForwardTimeout:
return new InspectionResult(InspectionDecision.Retry, "ForwardTimeout");
case ClientMessage.OperationResult.WrongExpectedVersion:
var err = util.format("Start transaction failed due to WrongExpectedVersion. Stream: %s, Expected version: %d.", this._stream, this._expectedVersion);
this.fail(new Error(err));
return new InspectionResult(InspectionDecision.EndOperation, "WrongExpectedVersion");
case ClientMessage.OperationResult.StreamDeleted:
this.fail(new Error("Stream deleted: " + this._stream));
return new InspectionResult(InspectionDecision.EndOperation, "StreamDeleted");
case ClientMessage.OperationResult.InvalidTransaction:
this.fail(new Error("Invalid transaction."));
return new InspectionResult(InspectionDecision.EndOperation, "InvalidTransaction");
case ClientMessage.OperationResult.AccessDenied:
this.fail(new Error(util.format("Write access denied for stream '%s'.", this._stream)));
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
default:
throw new Error(util.format("Unexpected OperationResult: %s.", response.result));
}
};
StartTransactionOperation.prototype._transformResponse = function(response) {
return new EventStoreTransaction(results.toNumber(response.transaction_id), this.userCredentials, this._parentConnection);
};
StartTransactionOperation.prototype.toString = function() {
return util.format("Stream: %s, ExpectedVersion: %d", this._stream, this._expectedVersion);
};
module.exports = StartTransactionOperation;

View File

@ -0,0 +1,265 @@
var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand');
var TcpFlags = require('../systemData/tcpFlags');
var InspectionDecision = require('../systemData/inspectionDecision');
var InspectionResult = require('./../systemData/inspectionResult');
var ClientMessage = require('../messages/clientMessage');
var TcpPackage = require('../systemData/tcpPackage');
var BufferSegment = require('../common/bufferSegment');
var results = require('../results');
var SubscriptionDropReason = require('../subscriptionDropReason');
//TODO: nodify eventAppeared and subscriptionDropped, should be emit on subscription
function SubscriptionOperation(
log, cb, streamId, resolveLinkTos, userCredentials, eventAppeared,
subscriptionDropped, verboseLogging, getConnection
) {
//TODO: validations
//Ensure.NotNull(log, "log");
//Ensure.NotNull(source, "source");
//Ensure.NotNull(eventAppeared, "eventAppeared");
//Ensure.NotNull(getConnection, "getConnection");
this._log = log;
this._cb = cb;
this._streamId = streamId || '';
this._resolveLinkTos = resolveLinkTos;
this._userCredentials = userCredentials;
this._eventAppeared = eventAppeared;
this._subscriptionDropped = subscriptionDropped || function() {};
this._verboseLogging = verboseLogging;
this._getConnection = getConnection;
this._correlationId = null;
this._unsubscribed = false;
this._subscription = null;
this._actionExecuting = false;
this._actionQueue = [];
}
SubscriptionOperation.prototype.subscribe = function(correlationId, connection) {
if (connection === null) throw new TypeError("connection is null.");
if (this._subscription != null || this._unsubscribed != 0)
return false;
this._correlationId = correlationId;
connection.enqueueSend(this._createSubscriptionPackage());
return true;
};
SubscriptionOperation.prototype._createSubscriptionPackage = function() {
throw new Error("SubscriptionOperation._createSubscriptionPackage abstract method called. " + this.constructor.name);
};
SubscriptionOperation.prototype.unsubscribe = function() {
this.dropSubscription(SubscriptionDropReason.UserInitiated, null, this._getConnection());
};
SubscriptionOperation.prototype._createUnsubscriptionPackage = function() {
var msg = new ClientMessage.UnsubscribeFromStream();
var data = new BufferSegment(msg.encode().toBuffer());
return new TcpPackage(TcpCommand.UnsubscribeFromStream, TcpFlags.None, this._correlationId, null, null, data);
};
SubscriptionOperation.prototype._inspectPackage = function(pkg) {
throw new Error("SubscriptionOperation._inspectPackage abstract method called." + this.constructor.name);
};
SubscriptionOperation.prototype.inspectPackage = function(pkg) {
try
{
var result = this._inspectPackage(pkg);
if (result !== null) {
return result;
}
switch (pkg.command)
{
case TcpCommand.StreamEventAppeared:
{
var dto = ClientMessage.StreamEventAppeared.decode(pkg.data.toBuffer());
this._onEventAppeared(new results.ResolvedEvent(dto.event));
return new InspectionResult(InspectionDecision.DoNothing, "StreamEventAppeared");
}
case TcpCommand.SubscriptionDropped:
{
var dto = ClientMessage.SubscriptionDropped.decode(pkg.data.toBuffer());
switch (dto.reason)
{
case ClientMessage.SubscriptionDropped.SubscriptionDropReason.Unsubscribed:
this.dropSubscription(SubscriptionDropReason.UserInitiated, null);
break;
case ClientMessage.SubscriptionDropped.SubscriptionDropReason.AccessDenied:
this.dropSubscription(SubscriptionDropReason.AccessDenied,
new Error(util.format("Subscription to '%s' failed due to access denied.", this._streamId || "<all>")));
break;
default:
if (this._verboseLogging) this._log.debug("Subscription dropped by server. Reason: %s.", dto.reason);
this.dropSubscription(SubscriptionDropReason.Unknown,
new Error(util.format("Unsubscribe reason: '%s'.", dto.reason)));
break;
}
return new InspectionResult(InspectionDecision.EndOperation, util.format("SubscriptionDropped: %s", dto.reason));
}
case TcpCommand.NotAuthenticated:
{
var message = pkg.data.toString();
this.dropSubscription(SubscriptionDropReason.NotAuthenticated,
new Error(message || "Authentication error"));
return new InspectionResult(InspectionDecision.EndOperation, "NotAuthenticated");
}
case TcpCommand.BadRequest:
{
var message = pkg.data.toString();
this.dropSubscription(SubscriptionDropReason.ServerError,
new Error("Server error: " + (message || "<no message>")));
return new InspectionResult(InspectionDecision.EndOperation, util.format("BadRequest: %s", message));
}
case TcpCommand.NotHandled:
{
if (this._subscription != null)
throw new Error("NotHandled command appeared while we already subscribed.");
var message = ClientMessage.NotHandled.decode(pkg.data.toBuffer());
switch (message.reason)
{
case ClientMessage.NotHandled.NotHandledReason.NotReady:
return new InspectionResult(InspectionDecision.Retry, "NotHandled - NotReady");
case ClientMessage.NotHandled.NotHandledReason.TooBusy:
return new InspectionResult(InspectionDecision.Retry, "NotHandled - TooBusy");
case ClientMessage.NotHandled.NotHandledReason.NotMaster:
var masterInfo = ClientMessage.NotHandled.MasterInfo.decode(message.additional_info);
return new InspectionResult(InspectionDecision.Reconnect, "NotHandled - NotMaster",
{host: masterInfo.external_tcp_address, port: masterInfo.external_tcp_port},
{host: masterInfo.external_secure_tcp_address, port: masterInfo.external_secure_tcp_port});
default:
this._log.error("Unknown NotHandledReason: %s.", message.reason);
return new InspectionResult(InspectionDecision.Retry, "NotHandled - <unknown>");
}
}
default:
{
this.dropSubscription(SubscriptionDropReason.ServerError,
new Error("Command not expected: " + TcpCommand.getName(pkg.command)));
return new InspectionResult(InspectionDecision.EndOperation, pkg.command);
}
}
}
catch (e)
{
this.dropSubscription(SubscriptionDropReason.Unknown, e);
return new InspectionResult(InspectionDecision.EndOperation, util.format("Exception - %s", e.Message));
}
};
SubscriptionOperation.prototype.connectionClosed = function() {
this.dropSubscription(SubscriptionDropReason.ConnectionClosed, new Error("Connection was closed."));
};
SubscriptionOperation.prototype.timeOutSubscription = function() {
if (this._subscription !== null)
return false;
this.dropSubscription(SubscriptionDropReason.SubscribingError, null);
return true;
};
SubscriptionOperation.prototype.dropSubscription = function(reason, err, connection) {
if (!this._unsubscribed)
{
this._unsubscribed = true;
if (this._verboseLogging)
this._log.debug("Subscription %s to %s: closing subscription, reason: %s, exception: %s...",
this._correlationId, this._streamId || "<all>", reason, err);
if (reason !== SubscriptionDropReason.UserInitiated)
{
if (err === null) throw new Error(util.format("No exception provided for subscription drop reason '%s", reason));
//TODO: this should be last thing to execute
this._cb(err);
}
if (reason === SubscriptionDropReason.UserInitiated && this._subscription !== null && connection !== null)
connection.enqueueSend(this._createUnsubscriptionPackage());
var self = this;
if (this._subscription !== null)
this._executeAction(function() { self._subscriptionDropped(self._subscription, reason, err); });
}
};
SubscriptionOperation.prototype._confirmSubscription = function(lastCommitPosition, lastEventNumber) {
if (lastCommitPosition < -1)
throw new Error(util.format("Invalid lastCommitPosition %s on subscription confirmation.", lastCommitPosition));
if (this._subscription !== null)
throw new Error("Double confirmation of subscription.");
if (this._verboseLogging)
this._log.debug("Subscription %s to %s: subscribed at CommitPosition: %d, EventNumber: %d.",
this._correlationId, this._streamId || "<all>", lastCommitPosition, lastEventNumber);
this._subscription = this._createSubscriptionObject(lastCommitPosition, lastEventNumber);
this._cb(null, this._subscription);
};
SubscriptionOperation.prototype._createSubscriptionObject = function(lastCommitPosition, lastEventNumber) {
throw new Error("SubscriptionOperation._createSubscriptionObject abstract method called. " + this.constructor.name);
};
SubscriptionOperation.prototype._onEventAppeared = function(e) {
if (this._unsubscribed)
return;
if (this._subscription === null) throw new Error("Subscription not confirmed, but event appeared!");
if (this._verboseLogging)
this._log.debug("Subscription %s to %s: event appeared (%s, %d, %s @ %j).",
this._correlationId, this._streamId || "<all>",
e.originalStreamId, e.originalEventNumber, e.originalEvent.eventType, e.originalPosition);
var self = this;
this._executeAction(function() { self._eventAppeared(self._subscription, e); });
};
SubscriptionOperation.prototype._executeAction = function(action) {
this._actionQueue.push(action);
if (!this._actionExecuting) {
this._actionExecuting = true;
setImmediate(this._executeActions.bind(this));
}
};
SubscriptionOperation.prototype._executeActions = function() {
//TODO: possible blocking loop for node.js
var action = this._actionQueue.shift();
while (action)
{
try
{
action();
}
catch (err)
{
this._log.error(err, "Exception during executing user callback: %s.", err.Message);
}
action = this._actionQueue.shift();
}
this._actionExecuting = false;
};
SubscriptionOperation.prototype.toString = function() {
return this.constructor.name;
};
module.exports = SubscriptionOperation;

View File

@ -0,0 +1,62 @@
var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand');
var InspectionDecision = require('../systemData/inspectionDecision');
var InspectionResult = require('./../systemData/inspectionResult');
var ClientMessage = require('../messages/clientMessage');
var OperationBase = require('../clientOperations/operationBase');
function TransactionalWriteOperation(log, cb, requireMaster, transactionId, events, userCredentials) {
OperationBase.call(this, log, cb, TcpCommand.TransactionWrite, TcpCommand.TransactionWriteCompleted, userCredentials);
this._responseType = ClientMessage.TransactionWriteCompleted;
this._requireMaster = requireMaster;
this._transactionId = transactionId;
this._events = events;
}
util.inherits(TransactionalWriteOperation, OperationBase);
TransactionalWriteOperation.prototype._createRequestDto = function() {
var dtos = this._events.map(function(ev) {
var eventId = new Buffer(uuid.parse(ev.eventId));
return new ClientMessage.NewEvent({
event_id: eventId, event_type: ev.type,
data_content_type: ev.isJson ? 1 : 0, metadata_content_type: 0,
data: ev.data, metadata: ev.metadata});
});
return new ClientMessage.TransactionWrite(this._transactionId, dtos, this._requireMaster);
};
TransactionalWriteOperation.prototype._inspectResponse = function(response) {
switch (response.result)
{
case ClientMessage.OperationResult.Success:
this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "Success");
case ClientMessage.OperationResult.PrepareTimeout:
return new InspectionResult(InspectionDecision.Retry, "PrepareTimeout");
case ClientMessage.OperationResult.CommitTimeout:
return new InspectionResult(InspectionDecision.Retry, "CommitTimeout");
case ClientMessage.OperationResult.ForwardTimeout:
return new InspectionResult(InspectionDecision.Retry, "ForwardTimeout");
case ClientMessage.OperationResult.AccessDenied:
this.fail(new Error("Write access denied."));
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
default:
throw new Error(util.format("Unexpected OperationResult: %s.", response.result));
}
};
TransactionalWriteOperation.prototype._transformResponse = function(response) {
return null;
};
TransactionalWriteOperation.prototype.toString = function() {
return util.format("TransactionId: %s", this._transactionId);
};
module.exports = TransactionalWriteOperation;

View File

@ -0,0 +1,55 @@
var util = require('util');
var SubscriptionOperation = require('./subscriptionOperation');
var ClientMessage = require('../messages/clientMessage');
var TcpPackage = require('../systemData/tcpPackage');
var TcpCommand = require('../systemData/tcpCommand');
var TcpFlags = require('../systemData/tcpFlags');
var BufferSegment = require('../common/bufferSegment');
var InspectionDecision = require('../systemData/inspectionDecision');
var InspectionResult = require('./../systemData/inspectionResult');
var results = require('../results');
var VolatileEventStoreSubscription = require('../volatileEventStoreConnection');
function VolatileSubscriptionOperation(
log, cb, streamId, resolveLinkTos, userCredentials, eventAppeared,
subscriptionDropped, verboseLogging, getConnection
) {
SubscriptionOperation.call(this, log, cb, streamId, resolveLinkTos, userCredentials, eventAppeared, subscriptionDropped, verboseLogging, getConnection);
}
util.inherits(VolatileSubscriptionOperation, SubscriptionOperation);
VolatileSubscriptionOperation.prototype._createSubscriptionPackage = function() {
var dto = new ClientMessage.SubscribeToStream(this._streamId, this._resolveLinkTos);
return new TcpPackage(TcpCommand.SubscribeToStream,
this._userCredentials != null ? TcpFlags.Authenticated : TcpFlags.None,
this._correlationId,
this._userCredentials != null ? this._userCredentials.username : null,
this._userCredentials != null ? this._userCredentials.password : null,
new BufferSegment(dto.encode().toBuffer()));
};
VolatileSubscriptionOperation.prototype._inspectPackage = function(pkg) {
try {
if (pkg.command == TcpCommand.SubscriptionConfirmation) {
var dto = ClientMessage.SubscriptionConfirmation.decode(pkg.data.toBuffer());
this._confirmSubscription(dto.last_commit_position, dto.last_event_number);
return new InspectionResult(InspectionDecision.Subscribed, "SubscriptionConfirmation");
}
if (pkg.command == TcpCommand.StreamEventAppeared) {
var dto = ClientMessage.StreamEventAppeared.decode(pkg.data.toBuffer());
this._onEventAppeared(new results.ResolvedEvent(dto.event));
return new InspectionResult(InspectionDecision.DoNothing, "StreamEventAppeared");
}
return null;
} catch(e) {
console.log(e.stack);
return null;
}
};
VolatileSubscriptionOperation.prototype._createSubscriptionObject = function(lastCommitPosition, lastEventNumber) {
return new VolatileEventStoreSubscription(this, this._streamId, lastCommitPosition, lastEventNumber);
};
module.exports = VolatileSubscriptionOperation;