Compare commits
52 Commits
Author | SHA1 | Date | |
---|---|---|---|
158ca2d0a7 | |||
34e928c440 | |||
212dd10ce2 | |||
cf196b9cc2 | |||
b9de9568a5 | |||
e2459321f6 | |||
5d6ec8b43f | |||
1558918692 | |||
0463d85cfe | |||
faed60b8ac | |||
006b5b4791 | |||
f97b7fff8e | |||
cc0b6259d1 | |||
a5a9f7c1ee | |||
8d7996e547 | |||
3ae8086e8d | |||
c45155811e | |||
3acfaafb2a | |||
52dee3584e | |||
dfd3ede298 | |||
c929681bbd | |||
4a28a23e20 | |||
eb5f95975b | |||
51adf5bcc8 | |||
0fbbecea06 | |||
9b9e202cec | |||
60f1d7252b | |||
9909bc053a | |||
269c89be29 | |||
99d9322d2a | |||
39e47396b2 | |||
454b150fb6 | |||
90e7ef1a9d | |||
9eb37ef42e | |||
14a05606ab | |||
7243163022 | |||
ed65fc8742 | |||
c6a22739f3 | |||
4a593053ad | |||
5569dffaf5 | |||
bc66e4bc3c | |||
39d5188f14 | |||
93e61434dd | |||
29ee990a0a | |||
4f066bf00e | |||
69842b8d44 | |||
51d5782387 | |||
830a317f0d | |||
f951a625f4 | |||
dd1302f641 | |||
4ea996781f | |||
b2504749ce |
11
.gitignore
vendored
11
.gitignore
vendored
@ -33,4 +33,13 @@ node_modules
|
|||||||
.node_repl_history
|
.node_repl_history
|
||||||
|
|
||||||
# InteliJ
|
# InteliJ
|
||||||
.idea
|
.idea/workspace.xml
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
lib/
|
||||||
|
.eslintrc.json
|
||||||
|
jsconfig.json
|
||||||
|
docs/
|
||||||
|
|
||||||
|
# Vagrant
|
||||||
|
.vagrant
|
||||||
|
1
.idea/.name
generated
Normal file
1
.idea/.name
generated
Normal file
@ -0,0 +1 @@
|
|||||||
|
eventstore-node
|
17
.idea/codeStyleSettings.xml
generated
Normal file
17
.idea/codeStyleSettings.xml
generated
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectCodeStyleSettingsManager">
|
||||||
|
<option name="PER_PROJECT_SETTINGS">
|
||||||
|
<value>
|
||||||
|
<codeStyleSettings language="JavaScript">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
|
<option name="TAB_SIZE" value="2" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
3
.idea/copyright/profiles_settings.xml
generated
Normal file
3
.idea/copyright/profiles_settings.xml
generated
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<component name="CopyrightManager">
|
||||||
|
<settings default="" />
|
||||||
|
</component>
|
6
.idea/encodings.xml
generated
Normal file
6
.idea/encodings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="PROJECT" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
12
.idea/eventstore-node.iml
generated
Normal file
12
.idea/eventstore-node.iml
generated
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/docs" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/lib" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
6
.idea/jsLibraryMappings.xml
generated
Normal file
6
.idea/jsLibraryMappings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaScriptLibraryMappings">
|
||||||
|
<includedPredefinedLibrary name="Node.js Core" />
|
||||||
|
</component>
|
||||||
|
</project>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/eventstore-node.iml" filepath="$PROJECT_DIR$/.idea/eventstore-node.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
127
README.md
127
README.md
@ -1,63 +1,128 @@
|
|||||||
# eventstore-node
|
# eventstore-node
|
||||||
A port of the EventStore .Net ClientAPI to Node.js
|
A port of the EventStore .Net ClientAPI to Node.js
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- Node.js >= 0.12
|
|
||||||
- Modules: [long](https://www.npmjs.org/package/long), [protobufjs](https://www.npmjs.org/package/protobufjs), [uuid](https://www.npmjs.org/package/uuid) (installed via `npm install`)
|
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Unstable
|
|
||||||
|
|
||||||
### Missing features:
|
### Missing features:
|
||||||
|
|
||||||
- Ssl connection
|
- Ssl connection
|
||||||
- Cluster connection
|
|
||||||
- Set system settings
|
- Set system settings
|
||||||
|
|
||||||
### Incomplete
|
### Areas to improve
|
||||||
|
|
||||||
- Typed errors: currently most errors are direct instance of Error, which is not practical for error handling
|
- Errors
|
||||||
- Performance: there's still some while loop in the code that could be problematic with node.js
|
- Use codes or types to differentiate between errors
|
||||||
- Tests: tests are only covering happy path scenarios for now
|
- Performance
|
||||||
|
- Performance hasn't been tested yet
|
||||||
|
- Tests
|
||||||
|
- Can always do with more tests
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
Install the client using npm
|
Install using `npm install eventstore-node`
|
||||||
|
|
||||||
npm install eventstore-node
|
### Dependencies
|
||||||
|
|
||||||
Examples
|
- Node.js >= 0.12
|
||||||
|
- Modules: [long](https://www.npmjs.org/package/long), [protobufjs](https://www.npmjs.org/package/protobufjs), [uuid](https://www.npmjs.org/package/uuid) (installed via `npm install`)
|
||||||
|
|
||||||
TODO
|
### API Documentation
|
||||||
|
|
||||||
|
#### Offline
|
||||||
|
|
||||||
## Porting .Net Task to Node.js
|
The offline documentation can be found in the module folder `./node_modules/eventstore-node/docs`.
|
||||||
|
|
||||||
I used Promise to replace .Net Task, so when executing an async command, i.e. appendToStream you'll have to wait for result/error like this:
|
#### Online
|
||||||
|
|
||||||
connection
|
The online documentation can be found at [https://dev.nicdex.com/eventstore-node/docs/](https://dev.nicdex.com/eventstore-node/docs/)
|
||||||
.appendToStream('myStream', client.expectedVersion.any, events, userCredentials)
|
|
||||||
.then(function(result) {
|
### Install & run an Eventstore on localhost
|
||||||
//Do something with the WriteResult here
|
|
||||||
})
|
See http://docs.geteventstore.com/introduction/3.9.0/ .
|
||||||
.catch(function(err) {
|
|
||||||
//Handle error here
|
### Example: Storing an event
|
||||||
});
|
|
||||||
|
Save to ```app.js:```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var esClient = require('eventstore-node');
|
||||||
|
var uuid = require('uuid');
|
||||||
|
|
||||||
|
var streamName = "testStream";
|
||||||
|
/*
|
||||||
|
Connecting to a single node using "tcp://localhost:1113"
|
||||||
|
- to connect to a cluster via dns discovery use "discover://my.host:2113"
|
||||||
|
- to connect to a cluster via gossip seeds use
|
||||||
|
[
|
||||||
|
new esClient.GossipSeed({host: '192.168.1.10', port: 2113}),
|
||||||
|
new esClient.GossipSeed({host: '192.168.1.11', port: 2113}),
|
||||||
|
new esClient.GossipSeed({host: '192.168.1.12', port: 2113})
|
||||||
|
]
|
||||||
|
*/
|
||||||
|
var connSettings = {}; // Use defaults
|
||||||
|
var esConnection = esClient.createConnection(connSettings, "tcp://localhost:1113");
|
||||||
|
esConnection.connect();
|
||||||
|
esConnection.once('connected', function (tcpEndPoint) {
|
||||||
|
console.log('Connected to eventstore at ' + tcpEndPoint.host + ":" + tcpEndPoint.port);
|
||||||
|
});
|
||||||
|
|
||||||
|
var eventId = uuid.v4();
|
||||||
|
var eventData = {
|
||||||
|
a : Math.random(),
|
||||||
|
b: uuid.v4()
|
||||||
|
};
|
||||||
|
var event = esClient.createJsonEventData(eventId, eventData, null, 'testEvent');
|
||||||
|
console.log("Appending...");
|
||||||
|
esConnection.appendToStream(streamName, esClient.expectedVersion.any, event)
|
||||||
|
.then(function(result) {
|
||||||
|
console.log("Stored event:", eventId);
|
||||||
|
console.log("Look for it at: http://localhost:2113/web/index.html#/streams/testStream");
|
||||||
|
esConnection.close();
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```json
|
||||||
|
npm install uuid
|
||||||
|
npm install eventstore-node
|
||||||
|
node app.js
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: Subscribing to events
|
||||||
|
|
||||||
|
```cd samples```
|
||||||
|
|
||||||
|
To subscribe to all events from now on (includes example of a filter which ignores events which we aren't interested in):
|
||||||
|
|
||||||
|
```node subscribe-all-events.js```
|
||||||
|
|
||||||
|
To catch up on all events ever and subscribe to all new ones from now on:
|
||||||
|
|
||||||
|
```node subscribe-catchup-all-events.js```
|
||||||
|
|
||||||
|
To generate a test event, open a separate console and run:
|
||||||
|
|
||||||
|
```node store-event.js```
|
||||||
|
|
||||||
## Running the tests
|
## Running the tests
|
||||||
|
|
||||||
To run the tests you will need
|
To run the tests it is recommended that you use an in-memory instance of the eventstore so you don't pollute your dev instance.
|
||||||
|
|
||||||
- To install the dependencies (`npm install`)
|
EventStore.ClusterNode.exe --memdb
|
||||||
- To install nodeunit (`npm install -g nodeunit`)
|
|
||||||
- Run an instance of EventStore >= 3.3.0 (competing consumers are required for test) on localhost:1113 (Download [here](https://geteventstore.com/downloads/))
|
|
||||||
|
|
||||||
To execute the tests suites simply run test with npm
|
To execute the tests suites simply run
|
||||||
|
|
||||||
npm test
|
npm test
|
||||||
|
|
||||||
|
## Porting .Net Task to Node.js
|
||||||
|
|
||||||
|
Any async commands returns a [Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise) object in replacement of .Net Task.
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Ported code is released under the MIT license, see [LICENSE](https://github.com/nicdex/eventstore-node/blob/master/LICENSE).
|
Ported code is released under the MIT license, see [LICENSE](https://github.com/nicdex/eventstore-node/blob/master/LICENSE).
|
||||||
|
87
Vagrantfile
vendored
Normal file
87
Vagrantfile
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# -*- mode: ruby -*-
|
||||||
|
# vi: set ft=ruby :
|
||||||
|
|
||||||
|
# All Vagrant configuration is done below. The "2" in Vagrant.configure
|
||||||
|
# configures the configuration version (we support older styles for
|
||||||
|
# backwards compatibility). Please don't change it unless you know what
|
||||||
|
# you're doing.
|
||||||
|
Vagrant.configure("2") do |config|
|
||||||
|
# The most common configuration options are documented and commented below.
|
||||||
|
# For a complete reference, please see the online documentation at
|
||||||
|
# https://docs.vagrantup.com.
|
||||||
|
|
||||||
|
config.vm.define "es_cluster_1" do |es_cluster_1|
|
||||||
|
es_cluster_1.vm.box = "ubuntu/trusty64"
|
||||||
|
|
||||||
|
es_cluster_1.vm.box_check_update = false
|
||||||
|
|
||||||
|
es_cluster_1.vm.network "private_network", ip: "192.168.33.10"
|
||||||
|
|
||||||
|
es_cluster_1.vm.provider "virtualbox" do |vb|
|
||||||
|
vb.memory = "512"
|
||||||
|
end
|
||||||
|
|
||||||
|
es_cluster_1.vm.provision "shell", inline: <<-SHELL
|
||||||
|
curl -s https://packagecloud.io/install/repositories/EventStore/EventStore-OSS/script.deb.sh | sudo bash
|
||||||
|
sudo apt install EventStore-OSS
|
||||||
|
sudo echo "---" > /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "RunProjections: None" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "IntIp: 192.168.33.10" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "ExtIp: 192.168.33.10" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "ClusterSize: 3" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "DiscoverViaDns: False" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "GossipSeed: ['192.168.33.11:2112','192.168.33.12:2112']" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo service eventstore start
|
||||||
|
SHELL
|
||||||
|
end
|
||||||
|
|
||||||
|
config.vm.define "es_cluster_2" do |es_cluster_2|
|
||||||
|
es_cluster_2.vm.box = "ubuntu/trusty64"
|
||||||
|
|
||||||
|
es_cluster_2.vm.box_check_update = false
|
||||||
|
|
||||||
|
es_cluster_2.vm.network "private_network", ip: "192.168.33.11"
|
||||||
|
|
||||||
|
es_cluster_2.vm.provider "virtualbox" do |vb|
|
||||||
|
vb.memory = "512"
|
||||||
|
end
|
||||||
|
|
||||||
|
es_cluster_2.vm.provision "shell", inline: <<-SHELL
|
||||||
|
curl -s https://packagecloud.io/install/repositories/EventStore/EventStore-OSS/script.deb.sh | sudo bash
|
||||||
|
sudo apt install EventStore-OSS
|
||||||
|
sudo echo "---" > /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "RunProjections: None" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "IntIp: 192.168.33.11" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "ExtIp: 192.168.33.11" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "ClusterSize: 3" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "DiscoverViaDns: False" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "GossipSeed: ['192.168.33.10:2112','192.168.33.12:2112']" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo service eventstore start
|
||||||
|
SHELL
|
||||||
|
end
|
||||||
|
|
||||||
|
config.vm.define "es_cluster_3" do |es_cluster_3|
|
||||||
|
es_cluster_3.vm.box = "ubuntu/trusty64"
|
||||||
|
|
||||||
|
es_cluster_3.vm.box_check_update = false
|
||||||
|
|
||||||
|
es_cluster_3.vm.network "private_network", ip: "192.168.33.12"
|
||||||
|
|
||||||
|
es_cluster_3.vm.provider "virtualbox" do |vb|
|
||||||
|
vb.memory = "512"
|
||||||
|
end
|
||||||
|
|
||||||
|
es_cluster_3.vm.provision "shell", inline: <<-SHELL
|
||||||
|
curl -s https://packagecloud.io/install/repositories/EventStore/EventStore-OSS/script.deb.sh | sudo bash
|
||||||
|
sudo apt install EventStore-OSS
|
||||||
|
sudo echo "---" > /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "RunProjections: None" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "IntIp: 192.168.33.12" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "ExtIp: 192.168.33.12" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "ClusterSize: 3" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "DiscoverViaDns: False" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo echo "GossipSeed: ['192.168.33.10:2112','192.168.33.11:2112']" >> /etc/eventstore/eventstore.conf
|
||||||
|
sudo service eventstore start
|
||||||
|
SHELL
|
||||||
|
end
|
||||||
|
end
|
277
index.d.ts
vendored
Normal file
277
index.d.ts
vendored
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
/// <reference types="node" />
|
||||||
|
/// <reference types="Long" />
|
||||||
|
|
||||||
|
export class Position {
|
||||||
|
constructor(commitPosition: number|Long, preparePosition: number|Long);
|
||||||
|
readonly commitPosition: Long;
|
||||||
|
readonly preparePosition: Long;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose classes
|
||||||
|
|
||||||
|
export class UserCredentials {
|
||||||
|
constructor(username: string, password: string);
|
||||||
|
readonly username: string;
|
||||||
|
readonly password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PersistentSubscriptionSettings {
|
||||||
|
constructor(resolveLinkTos: boolean, startFrom: number, extraStatistics: boolean, messageTimeout: number,
|
||||||
|
maxRetryCount: number, liveBufferSize: number, readBatchSize: number, historyBufferSize: number,
|
||||||
|
checkPointAfter: number, minCheckPointCount: number, maxCheckPointCount: number,
|
||||||
|
maxSubscriberCount: number, namedConsumerStrategy: string)
|
||||||
|
static create();
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace SystemConsumerStrategies {
|
||||||
|
const DispatchToSingle: string;
|
||||||
|
const RoundRobin: string;
|
||||||
|
const Pinned: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GossipSeed {
|
||||||
|
constructor(endPoint: TcpEndPoint, hostHeader: string);
|
||||||
|
readonly endPoint: TcpEndPoint;
|
||||||
|
readonly hostHeader: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose errors
|
||||||
|
export class WrongExpectedVersionError {
|
||||||
|
readonly name: string;
|
||||||
|
readonly action: string;
|
||||||
|
readonly message: string;
|
||||||
|
readonly stream?: string;
|
||||||
|
readonly expectedVersion?: number;
|
||||||
|
readonly transactionId?: Long;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StreamDeletedError {
|
||||||
|
readonly message: string;
|
||||||
|
readonly stream?: string;
|
||||||
|
readonly transactionId?: Long;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AccessDeniedError {
|
||||||
|
readonly name: string;
|
||||||
|
readonly action: string;
|
||||||
|
readonly message: string;
|
||||||
|
readonly stream?: string;
|
||||||
|
readonly transactionId?: Long;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose enums/constants
|
||||||
|
|
||||||
|
export namespace expectedVersion {
|
||||||
|
const any: number;
|
||||||
|
const noStream: number;
|
||||||
|
const emptyStream: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace positions {
|
||||||
|
const start: Position;
|
||||||
|
const end: Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
// systemMetadata
|
||||||
|
// eventReadStatus
|
||||||
|
// sliceReadStatus
|
||||||
|
|
||||||
|
// Expose loggers
|
||||||
|
|
||||||
|
export interface Logger {
|
||||||
|
debug(fmt: string, ...args: any[]): void;
|
||||||
|
info(fmt: string, ...args: any[]): void;
|
||||||
|
error(fmt: string, ...args: any[]): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NoOpLogger implements Logger {
|
||||||
|
constructor()
|
||||||
|
debug(fmt: string, ...args: any[]): void;
|
||||||
|
info(fmt: string, ...args: any[]): void;
|
||||||
|
error(fmt: string, ...args: any[]): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FileLogger implements Logger {
|
||||||
|
constructor(filePath: string, append: boolean);
|
||||||
|
debug(fmt: string, ...args: any[]): void;
|
||||||
|
info(fmt: string, ...args: any[]): void;
|
||||||
|
error(fmt: string, ...args: any[]): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
export interface WriteResult {
|
||||||
|
readonly nextExpectedVersion: number;
|
||||||
|
readonly logPosition: Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RecordedEvent {
|
||||||
|
readonly eventStreamId: string;
|
||||||
|
readonly eventId: string;
|
||||||
|
readonly eventNumber: number;
|
||||||
|
readonly eventType: string;
|
||||||
|
readonly createdEpoch: number;
|
||||||
|
readonly data?: Buffer;
|
||||||
|
readonly metadata?: Buffer;
|
||||||
|
readonly isJson: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResolvedEvent {
|
||||||
|
readonly event?: RecordedEvent;
|
||||||
|
readonly link?: RecordedEvent;
|
||||||
|
readonly originalEvent?: RecordedEvent;
|
||||||
|
readonly isResolved: boolean;
|
||||||
|
readonly originalPosition?: Position;
|
||||||
|
readonly originalStreamId: string;
|
||||||
|
readonly originalEventNumber: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StreamEventsSlice {
|
||||||
|
readonly status: string; // TODO: enum
|
||||||
|
readonly stream: string;
|
||||||
|
readonly fromEventNumber: number;
|
||||||
|
readonly readDirection: string; // TODO: enum
|
||||||
|
readonly events: ResolvedEvent[];
|
||||||
|
readonly nextEventNumber: number;
|
||||||
|
readonly lastEventNumber: number;
|
||||||
|
readonly isEndOfStream: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AllEventsSlice {
|
||||||
|
readonly readDirection: string; // TODO enum
|
||||||
|
readonly fromPosition: Position;
|
||||||
|
readonly nextPosition: Position;
|
||||||
|
readonly events: ResolvedEvent[];
|
||||||
|
readonly isEndOfStream: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteResult {
|
||||||
|
readonly logPosition: Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EventStoreTransaction {
|
||||||
|
readonly transactionId: number;
|
||||||
|
commit(): Promise<WriteResult>;
|
||||||
|
write(eventOrEvents: EventData | EventData[]): Promise<void>;
|
||||||
|
rollback(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EventReadResult {
|
||||||
|
readonly status: string;
|
||||||
|
readonly stream: string;
|
||||||
|
readonly eventNumber: number;
|
||||||
|
readonly event: ResolvedEvent | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EventStoreSubscription {
|
||||||
|
readonly isSubscribedToAll: boolean;
|
||||||
|
readonly streamId: string;
|
||||||
|
readonly lastCommitPosition: Position;
|
||||||
|
readonly lastEventNumber: number;
|
||||||
|
|
||||||
|
close(): void;
|
||||||
|
unsubscribe(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EventStoreCatchUpSubscription {
|
||||||
|
start(): void;
|
||||||
|
stop(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RawStreamMetadataResult {
|
||||||
|
readonly stream: string;
|
||||||
|
readonly isStreamDeleted: boolean;
|
||||||
|
readonly metastreamVersion: number;
|
||||||
|
readonly streamMetadata: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callbacks
|
||||||
|
export interface EventAppearedCallback<TSubscription> {
|
||||||
|
(subscription: TSubscription, event: ResolvedEvent): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LiveProcessingStartedCallback {
|
||||||
|
(subscription: EventStoreCatchUpSubscription): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SubscriptionDroppedCallback<TSubscription> {
|
||||||
|
(subscription: TSubscription, reason: string, error?: Error): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TcpEndPoint {
|
||||||
|
port: number;
|
||||||
|
host: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EventData {
|
||||||
|
readonly eventId: string;
|
||||||
|
readonly type: string;
|
||||||
|
readonly isJson: boolean;
|
||||||
|
readonly data: Buffer;
|
||||||
|
readonly metadata: Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EventStoreNodeConnection {
|
||||||
|
connect(): Promise<void>;
|
||||||
|
close(): void;
|
||||||
|
// write actions
|
||||||
|
deleteStream(stream: string, expectedVersion: number, hardDelete?: boolean, userCredentials?: UserCredentials): Promise<DeleteResult>;
|
||||||
|
appendToStream(stream: string, expectedVersion: number, eventOrEvents: EventData | EventData[], userCredentials?: UserCredentials): Promise<WriteResult>;
|
||||||
|
startTransaction(stream: string, expectedVersion: number, userCredentials?: UserCredentials): Promise<EventStoreTransaction>;
|
||||||
|
continueTransaction(transactionId: number, userCredentials?: UserCredentials): EventStoreTransaction;
|
||||||
|
// read actions
|
||||||
|
readEvent(stream: string, eventNumber: number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<EventReadResult>;
|
||||||
|
readStreamEventsForward(stream: string, start: number, count: number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<StreamEventsSlice>;
|
||||||
|
readStreamEventsBackward(stream: string, start: number, count: number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<StreamEventsSlice>;
|
||||||
|
readAllEventsForward(position: Position, maxCount: number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<AllEventsSlice>;
|
||||||
|
readAllEventsBackward(position: Position, maxCount: number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<AllEventsSlice>;
|
||||||
|
// subscription actions
|
||||||
|
subscribeToStream(stream: string, resolveLinkTos: boolean, eventAppeared: EventAppearedCallback<EventStoreSubscription>, subscriptionDropped?: SubscriptionDroppedCallback<EventStoreSubscription>, userCredentials?: UserCredentials): Promise<EventStoreSubscription>;
|
||||||
|
subscribeToStreamFrom(stream: string, lastCheckpoint: number | null, resolveLinkTos: boolean, eventAppeared: EventAppearedCallback<EventStoreCatchUpSubscription>, liveProcessingStarted?: LiveProcessingStartedCallback, subscriptionDropped?: SubscriptionDroppedCallback<EventStoreCatchUpSubscription>, userCredentials?: UserCredentials, readBatchSize?: number): EventStoreCatchUpSubscription;
|
||||||
|
subscribeToAll(resolveLinkTos: boolean, eventAppeared: EventAppearedCallback<EventStoreSubscription>, subscriptionDropped?: SubscriptionDroppedCallback<EventStoreSubscription>, userCredentials?: UserCredentials): Promise<EventStoreSubscription>;
|
||||||
|
subscribeToAllFrom(lastCheckpoint: Position | null, resolveLinkTos: boolean, eventAppeared: EventAppearedCallback<EventStoreCatchUpSubscription>, liveProcessingStarted?: LiveProcessingStartedCallback, subscriptionDropped?: SubscriptionDroppedCallback<EventStoreCatchUpSubscription>, userCredentials?: UserCredentials, readBatchSize?: number): EventStoreCatchUpSubscription;
|
||||||
|
// metadata actions
|
||||||
|
setStreamMetadataRaw(stream: string, expectedMetastreamVersion: number, metadata: any, userCredentials?: UserCredentials): Promise<WriteResult>;
|
||||||
|
getStreamMetadataRaw(stream: string, userCredentials?: UserCredentials): Promise<RawStreamMetadataResult>;
|
||||||
|
|
||||||
|
on(event: "connected" | "disconnected" | "reconnecting" | "closed" | "error", listener: (arg: Error | string | TcpEndPoint) => void): this;
|
||||||
|
once(event: "connected" | "disconnected" | "reconnecting" | "closed" | "error", listener: (arg: Error | string | TcpEndPoint) => void): this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose helper functions
|
||||||
|
|
||||||
|
export interface ConnectionSettings {
|
||||||
|
log?: Logger,
|
||||||
|
verboseLogging?: boolean,
|
||||||
|
|
||||||
|
maxQueueSize?: number,
|
||||||
|
maxConcurrentItems?: number,
|
||||||
|
maxRetries?: number,
|
||||||
|
maxReconnections?: number,
|
||||||
|
|
||||||
|
requireMaster?: boolean,
|
||||||
|
|
||||||
|
reconnectionDelay?: number,
|
||||||
|
operationTimeout?: number,
|
||||||
|
operationTimeoutCheckPeriod?: number,
|
||||||
|
|
||||||
|
defaultUserCredentials?: UserCredentials,
|
||||||
|
useSslConnection?: boolean,
|
||||||
|
targetHost?: TcpEndPoint,
|
||||||
|
validateServer?: boolean,
|
||||||
|
|
||||||
|
failOnNoServerResponse?: boolean,
|
||||||
|
heartbeatInterval?: number,
|
||||||
|
heartbeatTimeout?: number,
|
||||||
|
clientConnectionTimeout?: number,
|
||||||
|
|
||||||
|
// Cluster Settings
|
||||||
|
clusterDns?: string,
|
||||||
|
maxDiscoverAttempts?: number,
|
||||||
|
externalGossipPort?: number,
|
||||||
|
gossipTimeout?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createConnection(settings: ConnectionSettings, endPointOrGossipSeed: string | TcpEndPoint | GossipSeed[], connectionName?: string): EventStoreNodeConnection;
|
||||||
|
export function createJsonEventData(eventId: string, event: any, metadata?: any, type?: string): EventData;
|
||||||
|
export function createEventData(eventId: string, type: string, isJson: boolean, data: Buffer, metadata?: Buffer): EventData;
|
5
index.js
5
index.js
@ -3,9 +3,4 @@
|
|||||||
* see README.md for more details
|
* see README.md for more details
|
||||||
* see LICENSE for license info
|
* see LICENSE for license info
|
||||||
*/
|
*/
|
||||||
/**
|
|
||||||
* TODO:
|
|
||||||
* library is heavy on number of files so it could have negative impact on load time
|
|
||||||
* we need a compiled (single file) version of the library
|
|
||||||
*/
|
|
||||||
module.exports = require('./lib/dist.js');
|
module.exports = require('./lib/dist.js');
|
||||||
|
5893
lib/dist.js
5893
lib/dist.js
File diff suppressed because it is too large
Load Diff
40
package.json
40
package.json
@ -1,10 +1,29 @@
|
|||||||
{
|
{
|
||||||
"name": "eventstore-node",
|
"name": "eventstore-node",
|
||||||
"version": "0.0.2",
|
"version": "0.0.28",
|
||||||
"description": "A port of the EventStore .Net ClientAPI to Node.js",
|
"description": "A port of the EventStore .Net ClientAPI to Node.js",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
"types": "index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "nodeunit"
|
"clean": "rm lib/dist.js",
|
||||||
|
"build": "webpack",
|
||||||
|
"pretest": "npm run build",
|
||||||
|
"test": "nodeunit",
|
||||||
|
"test-debug": "TESTS_VERBOSE_LOGGING=1 nodeunit",
|
||||||
|
"prepublish": "npm run build && npm run gendocs",
|
||||||
|
"gendocs": "rm -rf docs && jsdoc src -r -d docs"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib",
|
||||||
|
"docs",
|
||||||
|
"src",
|
||||||
|
"index.js",
|
||||||
|
"index.d.ts"
|
||||||
|
],
|
||||||
|
"directories": {
|
||||||
|
"lib": "lib",
|
||||||
|
"doc": "docs",
|
||||||
|
"example": "samples"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -22,12 +41,15 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/nicdex/eventstore-node#readme",
|
"homepage": "https://github.com/nicdex/eventstore-node#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"long": "^3.0.3",
|
"@types/long": "^3.0.31",
|
||||||
"protobufjs": "^5.0.1",
|
"@types/node": "^6.0.47",
|
||||||
"uuid": "^2.0.1"
|
"long": "^3.2",
|
||||||
|
"protobufjs": "^5.0",
|
||||||
|
"uuid": "^2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"devDependencies": {
|
||||||
"node": ">=0.12"
|
"jsdoc": "^3.4.2",
|
||||||
},
|
"nodeunit": "^0.10.2",
|
||||||
"engineStrict": true
|
"webpack": "^1.13.2"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
39
samples/simple.js
Normal file
39
samples/simple.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
var client = require('../src/client');
|
||||||
|
var uuid = require('uuid');
|
||||||
|
|
||||||
|
var settings = {
|
||||||
|
verboseLogging: true,
|
||||||
|
log: new client.FileLogger('./simple-verbose.log')
|
||||||
|
};
|
||||||
|
var gossipSeeds = [
|
||||||
|
new client.GossipSeed({host: '192.168.33.10', port: 2113}),
|
||||||
|
new client.GossipSeed({host: '192.168.33.11', port: 2113}),
|
||||||
|
new client.GossipSeed({host: '192.168.33.12', port: 2113})
|
||||||
|
];
|
||||||
|
var conn = client.createConnection(settings, gossipSeeds);
|
||||||
|
conn.connect()
|
||||||
|
.catch(function (err) {
|
||||||
|
console.log(err);
|
||||||
|
//process.exit(-1);
|
||||||
|
});
|
||||||
|
conn.on('connected', function (endPoint) {
|
||||||
|
console.log('connected to endPoint', endPoint);
|
||||||
|
//Start some work
|
||||||
|
setInterval(function () {
|
||||||
|
conn.appendToStream('test-' + uuid.v4(), client.expectedVersion.noStream, [
|
||||||
|
client.createJsonEventData(uuid.v4(), {abc: 123}, null, 'MyEvent')
|
||||||
|
]).then(function (writeResult) {
|
||||||
|
console.log(writeResult);
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
conn.on('error', function (err) {
|
||||||
|
console.log('Error occurred on connection:', err);
|
||||||
|
});
|
||||||
|
conn.on('closed', function (reason) {
|
||||||
|
console.log('Connection closed, reason:', reason);
|
||||||
|
//process.exit(-1);
|
||||||
|
});
|
||||||
|
process.stdin.setRawMode(true);
|
||||||
|
process.stdin.resume();
|
||||||
|
process.stdin.on('data', process.exit.bind(process, 0));
|
40
samples/store-event.js
Normal file
40
samples/store-event.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
var esClient = require('../src/client'); // When running in 'eventstore-node/samples' folder.
|
||||||
|
// var esClient = require('eventstore-node'); // Otherwise
|
||||||
|
var uuid = require('uuid');
|
||||||
|
|
||||||
|
var esConnection = esClient.createConnection({}, {"hostname": "localhost", "port": 1113});
|
||||||
|
esConnection.connect();
|
||||||
|
esConnection.once('connected', function (tcpEndPoint) {
|
||||||
|
console.log('Connected to eventstore at ' + tcpEndPoint.host + ":" + tcpEndPoint.port);
|
||||||
|
var userId = uuid.v4();
|
||||||
|
// This event could happen as a result of (e.g.) a 'CreateUser(id, username, password)' command.
|
||||||
|
var userCreatedEvent = {
|
||||||
|
id: userId,
|
||||||
|
username: "user" + uuid.v4().substring(0,6), // Hard-to-spell exotic username.
|
||||||
|
password: Math.random().toString() // Hard-to-guess password.
|
||||||
|
};
|
||||||
|
var eventId = uuid.v4();
|
||||||
|
var event = esClient.createJsonEventData(eventId, userCreatedEvent, null, "UserCreated");
|
||||||
|
// Every user has her/his own stream of events:
|
||||||
|
var streamName = "user-" + userId;
|
||||||
|
console.log("Storing event. Look for it at http://localhost:2113/web/index.html#/streams/user-" + userId);
|
||||||
|
esConnection.appendToStream(streamName, esClient.expectedVersion.any, event)
|
||||||
|
.then(function(result) {
|
||||||
|
console.log("Event stored.");
|
||||||
|
process.exit(0);
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
console.log(err);
|
||||||
|
process.exit(-1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
esConnection.on('error', function (err) {
|
||||||
|
console.log('Error occurred on connection:', err);
|
||||||
|
process.exit(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
esConnection.on('closed', function (reason) {
|
||||||
|
console.log('Connection closed, reason:', reason);
|
||||||
|
process.exit(-1);
|
||||||
|
});
|
48
samples/subscribe-all-events.js
Normal file
48
samples/subscribe-all-events.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Subscribe to all new events on the $all stream. Filter out any which aren't about "user" aggregates.
|
||||||
|
|
||||||
|
var esClient = require('../src/client'); // When running in 'eventstore-node/samples' folder.
|
||||||
|
// var esClient = require('eventstore-node'); // Otherwise
|
||||||
|
|
||||||
|
const credentialsForAllEventsStream = new esClient.UserCredentials("admin", "changeit");
|
||||||
|
const resolveLinkTos = false;
|
||||||
|
|
||||||
|
var esConnection = esClient.createConnection({}, {"hostname": "localhost", "port": 1113});
|
||||||
|
esConnection.connect();
|
||||||
|
esConnection.once('connected', function (tcpEndPoint) {
|
||||||
|
console.log('Connected to eventstore at ' + tcpEndPoint.host + ":" + tcpEndPoint.port);
|
||||||
|
esConnection.subscribeToAll(resolveLinkTos, eventAppeared, subscriptionDropped, credentialsForAllEventsStream)
|
||||||
|
.then(function(subscription) {
|
||||||
|
console.log("subscription.isSubscribedToAll: " + subscription.isSubscribedToAll);
|
||||||
|
console.log("(To generate a test event, try running 'node store-event.js' in a separate console.)")
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function belongsToAUserAggregate(event) {
|
||||||
|
return event.originalEvent.eventStreamId.startsWith("user-")
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventAppeared(subscription, event) {
|
||||||
|
// Ignore all events which aren't about users:
|
||||||
|
if(belongsToAUserAggregate(event)) {
|
||||||
|
var aggregateId = event.originalEvent.eventStreamId;
|
||||||
|
var eventId = event.originalEvent.eventId;
|
||||||
|
var eventType = event.originalEvent.eventType;
|
||||||
|
console.log(aggregateId, eventType, eventId);
|
||||||
|
console.log(event.originalEvent.data.toString() + "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function subscriptionDropped(subscription, reason, error) {
|
||||||
|
if (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
console.log('Subscription dropped.');
|
||||||
|
}
|
||||||
|
|
||||||
|
esConnection.on('error', function (err) {
|
||||||
|
console.log('Error occurred on connection:', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
esConnection.on('closed', function (reason) {
|
||||||
|
console.log('Connection closed, reason:', reason);
|
||||||
|
});
|
51
samples/subscribe-catchup-all-events.js
Normal file
51
samples/subscribe-catchup-all-events.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Subscribe to all events on the $all stream. Catch up from the beginning, then listen for any new events as they occur.
|
||||||
|
// This can be used (e.g.) for subscribers which populate read models.
|
||||||
|
|
||||||
|
var esClient = require('../src/client'); // When running in 'eventstore-node/samples' folder.
|
||||||
|
// var esClient = require('eventstore-node'); // Otherwise
|
||||||
|
|
||||||
|
const credentialsForAllEventsStream = new esClient.UserCredentials("admin", "changeit");
|
||||||
|
|
||||||
|
var esConnection = esClient.createConnection({}, {"hostname": "localhost", "port": 1113});
|
||||||
|
esConnection.connect();
|
||||||
|
esConnection.once('connected', function (tcpEndPoint) {
|
||||||
|
console.log('Connected to eventstore at ' + tcpEndPoint.host + ":" + tcpEndPoint.port);
|
||||||
|
var subscription = esConnection.subscribeToAllFrom(null, true, eventAppeared, liveProcessingStarted, subscriptionDropped, credentialsForAllEventsStream);
|
||||||
|
console.log("subscription.isSubscribedToAll: " + subscription.isSubscribedToAll);
|
||||||
|
});
|
||||||
|
|
||||||
|
function eventAppeared(subscription, event) {
|
||||||
|
// This is where to filter out events which the subscriber isn't interested in.
|
||||||
|
// For an example, see 'subscribe-all-events.js'.
|
||||||
|
console.log(event.originalEvent.eventStreamId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function subscriptionDropped(subscription, reason, error) {
|
||||||
|
if (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
console.log('Subscription dropped.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function liveProcessingStarted() {
|
||||||
|
console.log("Caught up with previously stored events. Listening for new events.");
|
||||||
|
console.log("(To generate a test event, try running 'node store-event.js' in a separate console.)")
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventAppeared(stream, event) {
|
||||||
|
console.log(event.originalEvent.eventStreamId, event.originalEvent.eventId, event.originalEvent.eventType);
|
||||||
|
// Data:
|
||||||
|
// console.log(event.originalEvent.data.toString());
|
||||||
|
|
||||||
|
// Position in the event stream. Can be persisted and used to catch up with missed events when re-starting subscribers instead of re-reading
|
||||||
|
// all events from the beginning.
|
||||||
|
// console.log(event.originalPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
esConnection.on('error', function (err) {
|
||||||
|
console.log('Error occurred on connection:', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
esConnection.on('closed', function (reason) {
|
||||||
|
console.log('Connection closed, reason:', reason);
|
||||||
|
});
|
@ -12,13 +12,15 @@ const positions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} eventId
|
* Create an EventData object from JavaScript event/metadata that will be serialized as json
|
||||||
* @param {object} event
|
* @public
|
||||||
* @param {object} [metadata]
|
* @param {string} eventId Event UUID
|
||||||
* @param {string} [type]
|
* @param {object} event Event object
|
||||||
|
* @param {object} [metadata] Event metadata
|
||||||
|
* @param {string} [type] Event type
|
||||||
* @returns {EventData}
|
* @returns {EventData}
|
||||||
*/
|
*/
|
||||||
function jsonEventDataFactory(eventId, event, metadata, type) {
|
function createJsonEventData(eventId, event, metadata, type) {
|
||||||
if (!event || typeof event !== 'object') throw new TypeError("data must be an object.");
|
if (!event || typeof event !== 'object') throw new TypeError("data must be an object.");
|
||||||
|
|
||||||
var eventBuf = new Buffer(JSON.stringify(event));
|
var eventBuf = new Buffer(JSON.stringify(event));
|
||||||
@ -27,34 +29,40 @@ function jsonEventDataFactory(eventId, event, metadata, type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} eventId
|
* Create an EventData object from event/metadata buffer(s)
|
||||||
* @param {string} type
|
* @public
|
||||||
* @param {boolean} isJson
|
* @param {string} eventId Event UUID
|
||||||
* @param {Buffer} data
|
* @param {string} type Event type
|
||||||
* @param {Buffer} [metadata]
|
* @param {boolean} isJson is buffer(s) content json
|
||||||
|
* @param {Buffer} data Data buffer
|
||||||
|
* @param {Buffer} [metadata] Metadata buffer
|
||||||
* @returns {EventData}
|
* @returns {EventData}
|
||||||
*/
|
*/
|
||||||
function eventDataFactory(eventId, type, isJson, data, metadata) {
|
function createEventData(eventId, type, isJson, data, metadata) {
|
||||||
return new EventData(eventId, type, isJson, data, metadata);
|
return new EventData(eventId, type, isJson, data, metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exporting classes
|
// Expose classes
|
||||||
module.exports.EventStoreConnection = require('./eventStoreConnection');
|
module.exports.Position = results.Position;
|
||||||
module.exports.UserCredentials = require('./systemData/userCredentials');
|
module.exports.UserCredentials = require('./systemData/userCredentials');
|
||||||
module.exports.EventData = EventData;
|
|
||||||
module.exports.PersistentSubscriptionSettings = require('./persistentSubscriptionSettings');
|
module.exports.PersistentSubscriptionSettings = require('./persistentSubscriptionSettings');
|
||||||
module.exports.SystemConsumerStrategies = require('./systemConsumerStrategies');
|
module.exports.SystemConsumerStrategies = require('./systemConsumerStrategies');
|
||||||
// Exporting errors
|
module.exports.GossipSeed = require('./gossipSeed');
|
||||||
|
module.exports.EventStoreConnection = require('./eventStoreConnection');
|
||||||
|
// Expose errors
|
||||||
module.exports.WrongExpectedVersionError = require('./errors/wrongExpectedVersionError');
|
module.exports.WrongExpectedVersionError = require('./errors/wrongExpectedVersionError');
|
||||||
module.exports.StreamDeletedError = require('./errors/streamDeletedError');
|
module.exports.StreamDeletedError = require('./errors/streamDeletedError');
|
||||||
module.exports.AccessDeniedError = require('./errors/accessDeniedError');
|
module.exports.AccessDeniedError = require('./errors/accessDeniedError');
|
||||||
// Exporting enums/constants
|
// Expose enums/constants
|
||||||
module.exports.expectedVersion = expectedVersion;
|
module.exports.expectedVersion = expectedVersion;
|
||||||
module.exports.positions = positions;
|
module.exports.positions = positions;
|
||||||
module.exports.systemMetadata = require('./common/systemMetadata');
|
module.exports.systemMetadata = require('./common/systemMetadata');
|
||||||
module.exports.eventReadStatus = results.EventReadStatus;
|
module.exports.eventReadStatus = results.EventReadStatus;
|
||||||
module.exports.sliceReadStatus = require('./sliceReadStatus');
|
module.exports.sliceReadStatus = require('./sliceReadStatus');
|
||||||
// Helper functions
|
// Expose loggers
|
||||||
module.exports.createConnection = module.exports.EventStoreConnection.create;
|
module.exports.NoopLogger = require('./common/log/noopLogger');
|
||||||
module.exports.createEventData = eventDataFactory;
|
module.exports.FileLogger = require('./common/log/fileLogger');
|
||||||
module.exports.createJsonEventData = jsonEventDataFactory;
|
// Expose Helper functions
|
||||||
|
module.exports.createConnection = require('./eventStoreConnection').create;
|
||||||
|
module.exports.createJsonEventData = createJsonEventData;
|
||||||
|
module.exports.createEventData = createEventData;
|
||||||
|
@ -6,6 +6,9 @@ var InspectionDecision = require('../systemData/inspectionDecision');
|
|||||||
var InspectionResult = require('./../systemData/inspectionResult');
|
var InspectionResult = require('./../systemData/inspectionResult');
|
||||||
var ClientMessage = require('../messages/clientMessage');
|
var ClientMessage = require('../messages/clientMessage');
|
||||||
var results = require('../results');
|
var results = require('../results');
|
||||||
|
var WrongExpectedVersionError = require('../errors/wrongExpectedVersionError');
|
||||||
|
var StreamDeletedError = require('../errors/streamDeletedError');
|
||||||
|
var AccessDeniedError = require('../errors/accessDeniedError');
|
||||||
|
|
||||||
var OperationBase = require('../clientOperations/operationBase');
|
var OperationBase = require('../clientOperations/operationBase');
|
||||||
|
|
||||||
@ -36,17 +39,16 @@ CommitTransactionOperation.prototype._inspectResponse = function(response) {
|
|||||||
case ClientMessage.OperationResult.ForwardTimeout:
|
case ClientMessage.OperationResult.ForwardTimeout:
|
||||||
return new InspectionResult(InspectionDecision.Retry, "ForwardTimeout");
|
return new InspectionResult(InspectionDecision.Retry, "ForwardTimeout");
|
||||||
case ClientMessage.OperationResult.WrongExpectedVersion:
|
case ClientMessage.OperationResult.WrongExpectedVersion:
|
||||||
var err = util.format("Commit transaction failed due to WrongExpectedVersion. TransactionID: %d.", this._transactionId);
|
this.fail(new WrongExpectedVersionError("Commit", this._transactionId));
|
||||||
this.fail(new Error(err));
|
|
||||||
return new InspectionResult(InspectionDecision.EndOperation, "WrongExpectedVersion");
|
return new InspectionResult(InspectionDecision.EndOperation, "WrongExpectedVersion");
|
||||||
case ClientMessage.OperationResult.StreamDeleted:
|
case ClientMessage.OperationResult.StreamDeleted:
|
||||||
this.fail(new Error("Stream deleted."));
|
this.fail(new StreamDeletedError(this._transactionId));
|
||||||
return new InspectionResult(InspectionDecision.EndOperation, "StreamDeleted");
|
return new InspectionResult(InspectionDecision.EndOperation, "StreamDeleted");
|
||||||
case ClientMessage.OperationResult.InvalidTransaction:
|
case ClientMessage.OperationResult.InvalidTransaction:
|
||||||
this.fail(new Error("Invalid transaction."));
|
this.fail(new Error("Invalid transaction."));
|
||||||
return new InspectionResult(InspectionDecision.EndOperation, "InvalidTransaction");
|
return new InspectionResult(InspectionDecision.EndOperation, "InvalidTransaction");
|
||||||
case ClientMessage.OperationResult.AccessDenied:
|
case ClientMessage.OperationResult.AccessDenied:
|
||||||
this.fail(new Error("Write access denied."));
|
this.fail(new AccessDeniedError("Write", this._transactionId));
|
||||||
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
|
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
|
||||||
default:
|
default:
|
||||||
throw new Error(util.format("Unexpected OperationResult: %s.", response.result));
|
throw new Error(util.format("Unexpected OperationResult: %s.", response.result));
|
||||||
|
@ -37,38 +37,38 @@ ConnectToPersistentSubscriptionOperation.prototype._createSubscriptionPackage =
|
|||||||
};
|
};
|
||||||
|
|
||||||
ConnectToPersistentSubscriptionOperation.prototype._inspectPackage = function(pkg) {
|
ConnectToPersistentSubscriptionOperation.prototype._inspectPackage = function(pkg) {
|
||||||
if (pkg.command == TcpCommand.PersistentSubscriptionConfirmation)
|
if (pkg.command === TcpCommand.PersistentSubscriptionConfirmation)
|
||||||
{
|
{
|
||||||
var dto = ClientMessage.PersistentSubscriptionConfirmation.decode(pkg.data.toBuffer());
|
var dto = ClientMessage.PersistentSubscriptionConfirmation.decode(pkg.data.toBuffer());
|
||||||
this._confirmSubscription(dto.last_commit_position, dto.last_event_number);
|
this._confirmSubscription(dto.last_commit_position, dto.last_event_number);
|
||||||
this._subscriptionId = dto.subscription_id;
|
this._subscriptionId = dto.subscription_id;
|
||||||
return new InspectionResult(InspectionDecision.Subscribed, "SubscriptionConfirmation");
|
return new InspectionResult(InspectionDecision.Subscribed, "SubscriptionConfirmation");
|
||||||
}
|
}
|
||||||
if (pkg.command == TcpCommand.PersistentSubscriptionStreamEventAppeared)
|
if (pkg.command === TcpCommand.PersistentSubscriptionStreamEventAppeared)
|
||||||
{
|
{
|
||||||
var dto = ClientMessage.PersistentSubscriptionStreamEventAppeared.decode(pkg.data.toBuffer());
|
var dto = ClientMessage.PersistentSubscriptionStreamEventAppeared.decode(pkg.data.toBuffer());
|
||||||
this._onEventAppeared(new results.ResolvedEvent(dto.event));
|
this._onEventAppeared(new results.ResolvedEvent(dto.event));
|
||||||
return new InspectionResult(InspectionDecision.DoNothing, "StreamEventAppeared");
|
return new InspectionResult(InspectionDecision.DoNothing, "StreamEventAppeared");
|
||||||
}
|
}
|
||||||
if (pkg.command == TcpCommand.SubscriptionDropped)
|
if (pkg.command === TcpCommand.SubscriptionDropped)
|
||||||
{
|
{
|
||||||
var dto = ClientMessage.SubscriptionDropped.decode(pkg.data.toBuffer());
|
var dto = ClientMessage.SubscriptionDropped.decode(pkg.data.toBuffer());
|
||||||
if (dto.reason == ClientMessage.SubscriptionDropped.SubscriptionDropReason.AccessDenied)
|
if (dto.reason === ClientMessage.SubscriptionDropped.SubscriptionDropReason.AccessDenied)
|
||||||
{
|
{
|
||||||
this.dropSubscription(SubscriptionDropReason.AccessDenied, new Error("You do not have access to the stream."));
|
this.dropSubscription(SubscriptionDropReason.AccessDenied, new Error("You do not have access to the stream."));
|
||||||
return new InspectionResult(InspectionDecision.EndOperation, "SubscriptionDropped");
|
return new InspectionResult(InspectionDecision.EndOperation, "SubscriptionDropped");
|
||||||
}
|
}
|
||||||
if (dto.reason == ClientMessage.SubscriptionDropped.SubscriptionDropReason.NotFound)
|
if (dto.reason === ClientMessage.SubscriptionDropped.SubscriptionDropReason.NotFound)
|
||||||
{
|
{
|
||||||
this.dropSubscription(SubscriptionDropReason.NotFound, new Error("Subscription not found"));
|
this.dropSubscription(SubscriptionDropReason.NotFound, new Error("Subscription not found"));
|
||||||
return new InspectionResult(InspectionDecision.EndOperation, "SubscriptionDropped");
|
return new InspectionResult(InspectionDecision.EndOperation, "SubscriptionDropped");
|
||||||
}
|
}
|
||||||
if (dto.reason == ClientMessage.SubscriptionDropped.SubscriptionDropReason.PersistentSubscriptionDeleted)
|
if (dto.reason === ClientMessage.SubscriptionDropped.SubscriptionDropReason.PersistentSubscriptionDeleted)
|
||||||
{
|
{
|
||||||
this.dropSubscription(SubscriptionDropReason.PersistentSubscriptionDeleted, new Error("Persistent subscription deleted."));
|
this.dropSubscription(SubscriptionDropReason.PersistentSubscriptionDeleted, new Error("Persistent subscription deleted."));
|
||||||
return new InspectionResult(InspectionDecision.EndOperation, "SubscriptionDropped");
|
return new InspectionResult(InspectionDecision.EndOperation, "SubscriptionDropped");
|
||||||
}
|
}
|
||||||
if (dto.reason == ClientMessage.SubscriptionDropped.SubscriptionDropReason.SubscriberMaxCountReached)
|
if (dto.reason === ClientMessage.SubscriptionDropped.SubscriptionDropReason.SubscriberMaxCountReached)
|
||||||
{
|
{
|
||||||
this.dropSubscription(SubscriptionDropReason.MaxSubscribersReached, new Error("Maximum subscribers reached."));
|
this.dropSubscription(SubscriptionDropReason.MaxSubscribersReached, new Error("Maximum subscribers reached."));
|
||||||
return new InspectionResult(InspectionDecision.EndOperation, "SubscriptionDropped");
|
return new InspectionResult(InspectionDecision.EndOperation, "SubscriptionDropped");
|
||||||
@ -111,10 +111,10 @@ ConnectToPersistentSubscriptionOperation.prototype.notifyEventsFailed = function
|
|||||||
action);
|
action);
|
||||||
|
|
||||||
var pkg = new TcpPackage(TcpCommand.PersistentSubscriptionNakEvents,
|
var pkg = new TcpPackage(TcpCommand.PersistentSubscriptionNakEvents,
|
||||||
this._userCredentials != null ? TcpFlags.Authenticated : TcpFlags.None,
|
this._userCredentials !== null ? TcpFlags.Authenticated : TcpFlags.None,
|
||||||
this._correlationId,
|
this._correlationId,
|
||||||
this._userCredentials != null ? this._userCredentials.username : null,
|
this._userCredentials !== null ? this._userCredentials.username : null,
|
||||||
this._userCredentials != null ? this._userCredentials.password : null,
|
this._userCredentials !== null ? this._userCredentials.password : null,
|
||||||
createBufferSegment(dto.toBuffer()));
|
createBufferSegment(dto.toBuffer()));
|
||||||
this._enqueueSend(pkg);
|
this._enqueueSend(pkg);
|
||||||
};
|
};
|
||||||
|
@ -39,7 +39,7 @@ CreatePersistentSubscriptionOperation.prototype._createRequestDto = function() {
|
|||||||
return new ClientMessage.CreatePersistentSubscription(this._groupName, this._stream, this._resolveLinkTos,
|
return new ClientMessage.CreatePersistentSubscription(this._groupName, this._stream, this._resolveLinkTos,
|
||||||
this._startFromBeginning, this._messageTimeoutMilliseconds, this._recordStatistics, this._liveBufferSize,
|
this._startFromBeginning, this._messageTimeoutMilliseconds, this._recordStatistics, this._liveBufferSize,
|
||||||
this._readBatchSize, this._bufferSize, this._maxRetryCount,
|
this._readBatchSize, this._bufferSize, this._maxRetryCount,
|
||||||
this._namedConsumerStrategy == SystemConsumerStrategies.RoundRobin, this._checkPointAfter,
|
this._namedConsumerStrategy === SystemConsumerStrategies.RoundRobin, this._checkPointAfter,
|
||||||
this._maxCheckPointCount, this._minCheckPointCount, this._maxSubscriberCount, this._namedConsumerStrategy);
|
this._maxCheckPointCount, this._minCheckPointCount, this._maxSubscriberCount, this._namedConsumerStrategy);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ OperationBase.prototype._succeed = function() {
|
|||||||
if (!this._completed) {
|
if (!this._completed) {
|
||||||
this._completed = true;
|
this._completed = true;
|
||||||
|
|
||||||
if (this._response != null)
|
if (this._response)
|
||||||
this._cb(null, this._transformResponse(this._response));
|
this._cb(null, this._transformResponse(this._response));
|
||||||
else
|
else
|
||||||
this._cb(new Error("No result."))
|
this._cb(new Error("No result."))
|
||||||
@ -75,7 +75,7 @@ OperationBase.prototype.inspectPackage = function(pkg) {
|
|||||||
case TcpCommand.NotHandled:
|
case TcpCommand.NotHandled:
|
||||||
return this._inspectNotHandled(pkg);
|
return this._inspectNotHandled(pkg);
|
||||||
default:
|
default:
|
||||||
return this._inspectUnexpectedCommand(package, this._responseCommand);
|
return this._inspectUnexpectedCommand(pkg, this._responseCommand);
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
this.fail(e);
|
this.fail(e);
|
||||||
@ -118,7 +118,7 @@ OperationBase.prototype._inspectNotHandled = function(pkg)
|
|||||||
|
|
||||||
case ClientMessage.NotHandled.NotHandledReason.NotMaster:
|
case ClientMessage.NotHandled.NotHandledReason.NotMaster:
|
||||||
var masterInfo = ClientMessage.NotHandled.MasterInfo.decode(message.additional_info);
|
var masterInfo = ClientMessage.NotHandled.MasterInfo.decode(message.additional_info);
|
||||||
return new new InspectionResult(InspectionDecision.Reconnect, "NotHandled - NotMaster",
|
return new InspectionResult(InspectionDecision.Reconnect, "NotHandled - NotMaster",
|
||||||
{host: masterInfo.external_tcp_address, port: masterInfo.external_tcp_port},
|
{host: masterInfo.external_tcp_address, port: masterInfo.external_tcp_port},
|
||||||
{host: masterInfo.external_secure_tcp_address, port: masterInfo.external_secure_tcp_port});
|
{host: masterInfo.external_secure_tcp_address, port: masterInfo.external_secure_tcp_port});
|
||||||
|
|
||||||
@ -130,13 +130,13 @@ OperationBase.prototype._inspectNotHandled = function(pkg)
|
|||||||
|
|
||||||
OperationBase.prototype._inspectUnexpectedCommand = function(pkg, expectedCommand)
|
OperationBase.prototype._inspectUnexpectedCommand = function(pkg, expectedCommand)
|
||||||
{
|
{
|
||||||
if (pkg.command == expectedCommand)
|
if (pkg.command === expectedCommand)
|
||||||
throw new Error("Command shouldn't be " + TcpCommand.getName(pkg.command));
|
throw new Error("Command shouldn't be " + TcpCommand.getName(pkg.command));
|
||||||
|
|
||||||
this.log.error("Unexpected TcpCommand received.\n"
|
this.log.error("Unexpected TcpCommand received.\n"
|
||||||
+ "Expected: %s, Actual: %s, Flags: %s, CorrelationId: %s\n"
|
+ "Expected: %s, Actual: %s, Flags: %s, CorrelationId: %s\n"
|
||||||
+ "Operation (%s): %s\n"
|
+ "Operation (%s): %s\n"
|
||||||
+"TcpPackage Data Dump:\n%j",
|
+ "TcpPackage Data Dump:\n%j",
|
||||||
expectedCommand, TcpCommand.getName(pkg.command), pkg.flags, pkg.correlationId,
|
expectedCommand, TcpCommand.getName(pkg.command), pkg.flags, pkg.correlationId,
|
||||||
this.constructor.name, this, pkg.data);
|
this.constructor.name, this, pkg.data);
|
||||||
|
|
||||||
|
@ -7,7 +7,9 @@ var InspectionResult = require('./../systemData/inspectionResult');
|
|||||||
var ClientMessage = require('../messages/clientMessage');
|
var ClientMessage = require('../messages/clientMessage');
|
||||||
var EventStoreTransaction = require('../eventStoreTransaction');
|
var EventStoreTransaction = require('../eventStoreTransaction');
|
||||||
var results = require('../results');
|
var results = require('../results');
|
||||||
|
var AccessDeniedError = require('../errors/accessDeniedError');
|
||||||
|
var WrongExpectedVersionError = require('../errors/wrongExpectedVersionError');
|
||||||
|
var StreamDeletedError = require('../errors/streamDeletedError');
|
||||||
var OperationBase = require('../clientOperations/operationBase');
|
var OperationBase = require('../clientOperations/operationBase');
|
||||||
|
|
||||||
function StartTransactionOperation(log, cb, requireMaster, stream, expectedVersion, parentConnection, userCredentials) {
|
function StartTransactionOperation(log, cb, requireMaster, stream, expectedVersion, parentConnection, userCredentials) {
|
||||||
@ -38,17 +40,16 @@ StartTransactionOperation.prototype._inspectResponse = function(response) {
|
|||||||
case ClientMessage.OperationResult.ForwardTimeout:
|
case ClientMessage.OperationResult.ForwardTimeout:
|
||||||
return new InspectionResult(InspectionDecision.Retry, "ForwardTimeout");
|
return new InspectionResult(InspectionDecision.Retry, "ForwardTimeout");
|
||||||
case ClientMessage.OperationResult.WrongExpectedVersion:
|
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 WrongExpectedVersionError("Start transaction", this._stream, this._expectedVersion));
|
||||||
this.fail(new Error(err));
|
|
||||||
return new InspectionResult(InspectionDecision.EndOperation, "WrongExpectedVersion");
|
return new InspectionResult(InspectionDecision.EndOperation, "WrongExpectedVersion");
|
||||||
case ClientMessage.OperationResult.StreamDeleted:
|
case ClientMessage.OperationResult.StreamDeleted:
|
||||||
this.fail(new Error("Stream deleted: " + this._stream));
|
this.fail(new StreamDeletedError(this._stream));
|
||||||
return new InspectionResult(InspectionDecision.EndOperation, "StreamDeleted");
|
return new InspectionResult(InspectionDecision.EndOperation, "StreamDeleted");
|
||||||
case ClientMessage.OperationResult.InvalidTransaction:
|
case ClientMessage.OperationResult.InvalidTransaction:
|
||||||
this.fail(new Error("Invalid transaction."));
|
this.fail(new Error("Invalid transaction."));
|
||||||
return new InspectionResult(InspectionDecision.EndOperation, "InvalidTransaction");
|
return new InspectionResult(InspectionDecision.EndOperation, "InvalidTransaction");
|
||||||
case ClientMessage.OperationResult.AccessDenied:
|
case ClientMessage.OperationResult.AccessDenied:
|
||||||
this.fail(new Error(util.format("Write access denied for stream '%s'.", this._stream)));
|
this.fail(new AccessDeniedError("Write", this._stream));
|
||||||
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
|
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
|
||||||
default:
|
default:
|
||||||
throw new Error(util.format("Unexpected OperationResult: %s.", response.result));
|
throw new Error(util.format("Unexpected OperationResult: %s.", response.result));
|
||||||
|
@ -46,7 +46,7 @@ SubscriptionOperation.prototype._enqueueSend = function(pkg) {
|
|||||||
SubscriptionOperation.prototype.subscribe = function(correlationId, connection) {
|
SubscriptionOperation.prototype.subscribe = function(correlationId, connection) {
|
||||||
if (connection === null) throw new TypeError("connection is null.");
|
if (connection === null) throw new TypeError("connection is null.");
|
||||||
|
|
||||||
if (this._subscription != null || this._unsubscribed != 0)
|
if (this._subscription !== null || this._unsubscribed)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this._correlationId = correlationId;
|
this._correlationId = correlationId;
|
||||||
@ -128,7 +128,7 @@ SubscriptionOperation.prototype.inspectPackage = function(pkg) {
|
|||||||
|
|
||||||
case TcpCommand.NotHandled:
|
case TcpCommand.NotHandled:
|
||||||
{
|
{
|
||||||
if (this._subscription != null)
|
if (this._subscription !== null)
|
||||||
throw new Error("NotHandled command appeared while we already subscribed.");
|
throw new Error("NotHandled command appeared while we already subscribed.");
|
||||||
|
|
||||||
var message = ClientMessage.NotHandled.decode(pkg.data.toBuffer());
|
var message = ClientMessage.NotHandled.decode(pkg.data.toBuffer());
|
||||||
@ -189,7 +189,6 @@ SubscriptionOperation.prototype.dropSubscription = function(reason, err, connect
|
|||||||
if (reason !== SubscriptionDropReason.UserInitiated && this._subscription === null)
|
if (reason !== SubscriptionDropReason.UserInitiated && this._subscription === null)
|
||||||
{
|
{
|
||||||
if (err === null) throw new Error(util.format("No exception provided for subscription drop reason '%s", reason));
|
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);
|
this._cb(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ var TcpCommand = require('../systemData/tcpCommand');
|
|||||||
var InspectionDecision = require('../systemData/inspectionDecision');
|
var InspectionDecision = require('../systemData/inspectionDecision');
|
||||||
var InspectionResult = require('./../systemData/inspectionResult');
|
var InspectionResult = require('./../systemData/inspectionResult');
|
||||||
var ClientMessage = require('../messages/clientMessage');
|
var ClientMessage = require('../messages/clientMessage');
|
||||||
|
var AccessDeniedError = require('../errors/accessDeniedError');
|
||||||
|
|
||||||
var OperationBase = require('../clientOperations/operationBase');
|
var OperationBase = require('../clientOperations/operationBase');
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ TransactionalWriteOperation.prototype._inspectResponse = function(response) {
|
|||||||
case ClientMessage.OperationResult.ForwardTimeout:
|
case ClientMessage.OperationResult.ForwardTimeout:
|
||||||
return new InspectionResult(InspectionDecision.Retry, "ForwardTimeout");
|
return new InspectionResult(InspectionDecision.Retry, "ForwardTimeout");
|
||||||
case ClientMessage.OperationResult.AccessDenied:
|
case ClientMessage.OperationResult.AccessDenied:
|
||||||
this.fail(new Error("Write access denied."));
|
this.fail(new AccessDeniedError("Write", "trx:" + this._transactionId));
|
||||||
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
|
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
|
||||||
default:
|
default:
|
||||||
throw new Error(util.format("Unexpected OperationResult: %s.", response.result));
|
throw new Error(util.format("Unexpected OperationResult: %s.", response.result));
|
||||||
|
@ -39,7 +39,7 @@ UpdatePersistentSubscriptionOperation.prototype._createRequestDto = function() {
|
|||||||
return new ClientMessage.UpdatePersistentSubscription(this._groupName, this._stream, this._resolveLinkTos,
|
return new ClientMessage.UpdatePersistentSubscription(this._groupName, this._stream, this._resolveLinkTos,
|
||||||
this._startFromBeginning, this._messageTimeoutMilliseconds, this._recordStatistics, this._liveBufferSize,
|
this._startFromBeginning, this._messageTimeoutMilliseconds, this._recordStatistics, this._liveBufferSize,
|
||||||
this._readBatchSize, this._bufferSize, this._maxRetryCount,
|
this._readBatchSize, this._bufferSize, this._maxRetryCount,
|
||||||
this._namedConsumerStrategy == SystemConsumerStrategies.RoundRobin, this._checkPointAfter,
|
this._namedConsumerStrategy === SystemConsumerStrategies.RoundRobin, this._checkPointAfter,
|
||||||
this._maxCheckPointCount, this._minCheckPointCount, this._maxSubscriberCount, this._namedConsumerStrategy);
|
this._maxCheckPointCount, this._minCheckPointCount, this._maxSubscriberCount, this._namedConsumerStrategy);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ var BufferSegment = require('../common/bufferSegment');
|
|||||||
var InspectionDecision = require('../systemData/inspectionDecision');
|
var InspectionDecision = require('../systemData/inspectionDecision');
|
||||||
var InspectionResult = require('./../systemData/inspectionResult');
|
var InspectionResult = require('./../systemData/inspectionResult');
|
||||||
var results = require('../results');
|
var results = require('../results');
|
||||||
var VolatileEventStoreSubscription = require('../volatileEventStoreConnection');
|
var VolatileEventStoreSubscription = require('../volatileEventStoreSubscription');
|
||||||
|
|
||||||
function VolatileSubscriptionOperation(
|
function VolatileSubscriptionOperation(
|
||||||
log, cb, streamId, resolveLinkTos, userCredentials, eventAppeared,
|
log, cb, streamId, resolveLinkTos, userCredentials, eventAppeared,
|
||||||
@ -22,21 +22,21 @@ util.inherits(VolatileSubscriptionOperation, SubscriptionOperation);
|
|||||||
VolatileSubscriptionOperation.prototype._createSubscriptionPackage = function() {
|
VolatileSubscriptionOperation.prototype._createSubscriptionPackage = function() {
|
||||||
var dto = new ClientMessage.SubscribeToStream(this._streamId, this._resolveLinkTos);
|
var dto = new ClientMessage.SubscribeToStream(this._streamId, this._resolveLinkTos);
|
||||||
return new TcpPackage(TcpCommand.SubscribeToStream,
|
return new TcpPackage(TcpCommand.SubscribeToStream,
|
||||||
this._userCredentials != null ? TcpFlags.Authenticated : TcpFlags.None,
|
this._userCredentials !== null ? TcpFlags.Authenticated : TcpFlags.None,
|
||||||
this._correlationId,
|
this._correlationId,
|
||||||
this._userCredentials != null ? this._userCredentials.username : null,
|
this._userCredentials !== null ? this._userCredentials.username : null,
|
||||||
this._userCredentials != null ? this._userCredentials.password : null,
|
this._userCredentials !== null ? this._userCredentials.password : null,
|
||||||
new BufferSegment(dto.toBuffer()));
|
new BufferSegment(dto.toBuffer()));
|
||||||
};
|
};
|
||||||
|
|
||||||
VolatileSubscriptionOperation.prototype._inspectPackage = function(pkg) {
|
VolatileSubscriptionOperation.prototype._inspectPackage = function(pkg) {
|
||||||
try {
|
try {
|
||||||
if (pkg.command == TcpCommand.SubscriptionConfirmation) {
|
if (pkg.command === TcpCommand.SubscriptionConfirmation) {
|
||||||
var dto = ClientMessage.SubscriptionConfirmation.decode(pkg.data.toBuffer());
|
var dto = ClientMessage.SubscriptionConfirmation.decode(pkg.data.toBuffer());
|
||||||
this._confirmSubscription(dto.last_commit_position, dto.last_event_number);
|
this._confirmSubscription(dto.last_commit_position, dto.last_event_number);
|
||||||
return new InspectionResult(InspectionDecision.Subscribed, "SubscriptionConfirmation");
|
return new InspectionResult(InspectionDecision.Subscribed, "SubscriptionConfirmation");
|
||||||
}
|
}
|
||||||
if (pkg.command == TcpCommand.StreamEventAppeared) {
|
if (pkg.command === TcpCommand.StreamEventAppeared) {
|
||||||
var dto = ClientMessage.StreamEventAppeared.decode(pkg.data.toBuffer());
|
var dto = ClientMessage.StreamEventAppeared.decode(pkg.data.toBuffer());
|
||||||
this._onEventAppeared(new results.ResolvedEvent(dto.event));
|
this._onEventAppeared(new results.ResolvedEvent(dto.event));
|
||||||
return new InspectionResult(InspectionDecision.DoNothing, "StreamEventAppeared");
|
return new InspectionResult(InspectionDecision.DoNothing, "StreamEventAppeared");
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Create a buffer segment
|
* Create a buffer segment
|
||||||
|
* @private
|
||||||
* @param {Buffer} buf
|
* @param {Buffer} buf
|
||||||
* @param {number} [offset]
|
* @param {number} [offset]
|
||||||
* @param {number} [count]
|
* @param {number} [count]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
|
* @private
|
||||||
* @constructor
|
* @constructor
|
||||||
* @property {number} length
|
* @property {number} length
|
||||||
*/
|
*/
|
||||||
|
@ -6,7 +6,7 @@ function FileLogger(filePath, append) {
|
|||||||
this._filePath = filePath;
|
this._filePath = filePath;
|
||||||
if (!append) {
|
if (!append) {
|
||||||
try {
|
try {
|
||||||
fs.unlink(filePath);
|
fs.unlinkSync(filePath);
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,13 @@ module.exports.isArrayOf = function(expectedType, value, name) {
|
|||||||
if (!Array.isArray(value))
|
if (!Array.isArray(value))
|
||||||
throw new TypeError(name + " should be an array.");
|
throw new TypeError(name + " should be an array.");
|
||||||
if (!value.every(function(x) { return x instanceof expectedType; }))
|
if (!value.every(function(x) { return x instanceof expectedType; }))
|
||||||
throw new TypeError([name, " should be an array of ", expectedType, "."].join(""));
|
throw new TypeError([name, " should be an array of ", expectedType.name, "."].join(""));
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.isTypeOf = function(expectedType, value, name) {
|
module.exports.isTypeOf = function(expectedType, value, name, nullAllowed) {
|
||||||
|
if (nullAllowed && value === null) return;
|
||||||
if (!(value instanceof expectedType))
|
if (!(value instanceof expectedType))
|
||||||
throw new TypeError([name, " should be of type '", expectedType, "'."].join(""));
|
throw new TypeError([name, " should be of type '", expectedType.name, "'", nullAllowed ? " or null": "", "."].join(""));
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.positive = function(value, name) {
|
module.exports.positive = function(value, name) {
|
||||||
|
254
src/core/clusterDnsEndPointDiscoverer.js
Normal file
254
src/core/clusterDnsEndPointDiscoverer.js
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
var http = require('http');
|
||||||
|
var util = require('util');
|
||||||
|
var dns = require('dns');
|
||||||
|
var GossipSeed = require('../gossipSeed');
|
||||||
|
|
||||||
|
function NodeEndPoints(tcpEndPoint, secureTcpEndPoint) {
|
||||||
|
if (tcpEndPoint === null && secureTcpEndPoint === null) throw new Error('Both endpoints are null.');
|
||||||
|
Object.defineProperties(this, {
|
||||||
|
tcpEndPoint: {
|
||||||
|
enumerable: true,
|
||||||
|
value: tcpEndPoint
|
||||||
|
},
|
||||||
|
secureTcpEndPoint: {
|
||||||
|
enumerable: true,
|
||||||
|
value: secureTcpEndPoint
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function ClusterDnsEndPointDiscoverer(log, clusterDns, maxDiscoverAttempts, managerExternalHttpPort, gossipSeeds, gossipTimeout) {
|
||||||
|
if (!clusterDns && (!gossipSeeds || gossipSeeds.length === 0)) throw new Error('Both clusterDns and gossipSeeds are null/empty.');
|
||||||
|
this._log = log;
|
||||||
|
this._clusterDns = clusterDns;
|
||||||
|
this._maxDiscoverAttempts = maxDiscoverAttempts;
|
||||||
|
this._managerExternalHttpPort = managerExternalHttpPort;
|
||||||
|
this._gossipSeeds = gossipSeeds;
|
||||||
|
this._gossipTimeout = gossipTimeout;
|
||||||
|
this._oldGossip = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClusterDnsEndPointDiscoverer.prototype.discover = function(failedTcpEndPoint) {
|
||||||
|
var attempt = 1;
|
||||||
|
var self = this;
|
||||||
|
function discover(resolve, reject) {
|
||||||
|
self._discoverEndPoint(failedTcpEndPoint)
|
||||||
|
.then(function (endPoints) {
|
||||||
|
if (!endPoints)
|
||||||
|
self._log.info(util.format("Discovering attempt %d/%d failed: no candidate found.", attempt, self._maxDiscoverAttempts));
|
||||||
|
return endPoints;
|
||||||
|
})
|
||||||
|
.catch(function (exc) {
|
||||||
|
self._log.info(util.format("Discovering attempt %d/%d failed with error: %s.\n%s", attempt, self._maxDiscoverAttempts, exc, exc.stack));
|
||||||
|
})
|
||||||
|
.then(function (endPoints) {
|
||||||
|
if (endPoints)
|
||||||
|
return resolve(endPoints);
|
||||||
|
if (attempt++ === self._maxDiscoverAttempts)
|
||||||
|
return reject(new Error('Failed to discover candidate in ' + self._maxDiscoverAttempts + ' attempts.'));
|
||||||
|
setTimeout(discover, 500, resolve, reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
discover(resolve, reject);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover Cluster endpoints
|
||||||
|
* @param {Object} failedTcpEndPoint
|
||||||
|
* @returns {Promise.<NodeEndPoints>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ClusterDnsEndPointDiscoverer.prototype._discoverEndPoint = function (failedTcpEndPoint) {
|
||||||
|
try {
|
||||||
|
var mainPromise = this._oldGossip
|
||||||
|
? Promise.resolve(this._getGossipCandidatesFromOldGossip(this._oldGossip, failedTcpEndPoint))
|
||||||
|
: this._getGossipCandidatesFromDns();
|
||||||
|
var self = this;
|
||||||
|
var j = 0;
|
||||||
|
return mainPromise.then(function (gossipCandidates) {
|
||||||
|
var loopPromise = Promise.resolve();
|
||||||
|
for (var i = 0; i < gossipCandidates.length; i++) {
|
||||||
|
loopPromise = loopPromise.then(function (endPoints) {
|
||||||
|
if (endPoints) return endPoints;
|
||||||
|
return self._tryGetGossipFrom(gossipCandidates[j++])
|
||||||
|
.then(function (gossip) {
|
||||||
|
if (!gossip || !gossip.members || gossip.members.length === 0)
|
||||||
|
return;
|
||||||
|
var bestNode = self._tryDetermineBestNode(gossip.members);
|
||||||
|
if (bestNode) {
|
||||||
|
self._oldGossip = gossip.members;
|
||||||
|
return bestNode;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return loopPromise;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return Promise.reject(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ClusterDnsEndPointDiscoverer.prototype._getGossipCandidatesFromOldGossip = function (oldGossip, failedTcpEndPoint) {
|
||||||
|
if (!failedTcpEndPoint) return this._arrangeGossipCandidates(oldGossip);
|
||||||
|
var gossipCandidates = oldGossip.filter(function(x) {
|
||||||
|
return !(x.externalTcpPort === failedTcpEndPoint.port && x.externalTcpIp === failedTcpEndPoint.host);
|
||||||
|
});
|
||||||
|
return this._arrangeGossipCandidates(gossipCandidates);
|
||||||
|
};
|
||||||
|
|
||||||
|
ClusterDnsEndPointDiscoverer.prototype._arrangeGossipCandidates = function (members) {
|
||||||
|
var result = new Array(members.length);
|
||||||
|
var i = -1;
|
||||||
|
var j = members.length;
|
||||||
|
for (var k = 0; k < members.length; ++k)
|
||||||
|
{
|
||||||
|
if (members[k].state === 'Manager')
|
||||||
|
result[--j] = new GossipSeed({host: members[k].externalHttpIp, port: members[k].externalHttpPort});
|
||||||
|
else
|
||||||
|
result[++i] = new GossipSeed({host: members[k].externalHttpIp, port: members[k].externalHttpPort});
|
||||||
|
}
|
||||||
|
this._randomShuffle(result, 0, i); // shuffle nodes
|
||||||
|
this._randomShuffle(result, j, members.length - 1); // shuffle managers
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
ClusterDnsEndPointDiscoverer.prototype._getGossipCandidatesFromDns = function () {
|
||||||
|
var self = this;
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
if (self._gossipSeeds && self._gossipSeeds.length > 0) {
|
||||||
|
var endpoints = self._gossipSeeds;
|
||||||
|
self._randomShuffle(endpoints, 0, endpoints.length - 1);
|
||||||
|
resolve(endpoints);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const dnsOptions = {
|
||||||
|
family: 4,
|
||||||
|
hints: dns.ADDRCONFIG | dns.V4MAPPED,
|
||||||
|
all: true
|
||||||
|
};
|
||||||
|
dns.lookup(self._clusterDns, dnsOptions, function (err, addresses) {
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
if (!addresses || addresses.length === 0) {
|
||||||
|
return reject(new Error('No result from dns lookup for ' + self._clusterDns));
|
||||||
|
}
|
||||||
|
var endpoints = addresses.map(function (x) {
|
||||||
|
return new GossipSeed({host: x.address, port: self._managerExternalHttpPort});
|
||||||
|
});
|
||||||
|
resolve(endpoints);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
ClusterDnsEndPointDiscoverer.prototype._tryGetGossipFrom = function (endPoint) {
|
||||||
|
var options = {
|
||||||
|
host: endPoint.endPoint.host,
|
||||||
|
port: endPoint.endPoint.port,
|
||||||
|
path: '/gossip?format=json'
|
||||||
|
};
|
||||||
|
if (endPoint.hostHeader) {
|
||||||
|
options.headers = {'Host': endPoint.hostHeader};
|
||||||
|
}
|
||||||
|
this._log.info('Try get gossip from', endPoint);
|
||||||
|
var self = this;
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var timedout = false;
|
||||||
|
http.request(options, function (res) {
|
||||||
|
if (timedout) return;
|
||||||
|
var result = '';
|
||||||
|
if (res.statusCode !== 200) {
|
||||||
|
self._log.info('Trying to get gossip from', endPoint, 'failed with status code:', res.statusCode);
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.on('data', function (chunk) {
|
||||||
|
result += chunk.toString();
|
||||||
|
});
|
||||||
|
res.on('end', function () {
|
||||||
|
try {
|
||||||
|
result = JSON.parse(result);
|
||||||
|
} catch (e) {
|
||||||
|
return resolve();
|
||||||
|
}
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.setTimeout(self._gossipTimeout, function () {
|
||||||
|
self._log.info('Trying to get gossip from', endPoint, 'timed out.');
|
||||||
|
timedout = true;
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.on('error', function (e) {
|
||||||
|
if (timedout) return;
|
||||||
|
self._log.info('Trying to get gossip from', endPoint, 'failed with error:', e);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.end();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const VNodeStates = {
|
||||||
|
'Initializing': 0,
|
||||||
|
'Unknown': 1,
|
||||||
|
'PreReplica': 2,
|
||||||
|
'CatchingUp': 3,
|
||||||
|
'Clone': 4,
|
||||||
|
'Slave': 5,
|
||||||
|
'PreMaster': 6,
|
||||||
|
'Master': 7,
|
||||||
|
'Manager': 8,
|
||||||
|
'ShuttingDown': 9,
|
||||||
|
'Shutdown': 10
|
||||||
|
};
|
||||||
|
|
||||||
|
ClusterDnsEndPointDiscoverer.prototype._tryDetermineBestNode = function (members) {
|
||||||
|
var notAllowedStates = [
|
||||||
|
'Manager',
|
||||||
|
'ShuttingDown',
|
||||||
|
'Shutdown'
|
||||||
|
];
|
||||||
|
var node = members
|
||||||
|
.filter(function (x) {
|
||||||
|
return (x.isAlive && notAllowedStates.indexOf(x.state) === -1);
|
||||||
|
})
|
||||||
|
.sort(function (a, b) {
|
||||||
|
return VNodeStates[b.state] - VNodeStates[a.state];
|
||||||
|
})[0];
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
//_log.Info("Unable to locate suitable node. Gossip info:\n{0}.", string.Join("\n", members.Select(x => x.ToString())));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var normTcp = {host: node.externalTcpIp, port: node.externalTcpPort};
|
||||||
|
var secTcp = node.externalSecureTcpPort > 0
|
||||||
|
? {host: externalTcpIp, port: node.externalSecureTcpPort}
|
||||||
|
: null;
|
||||||
|
this._log.info(util.format("Discovering: found best choice [%j,%j] (%s).", normTcp, secTcp === null ? "n/a" : secTcp, node.state));
|
||||||
|
return new NodeEndPoints(normTcp, secTcp);
|
||||||
|
};
|
||||||
|
|
||||||
|
function rndNext(min, max) {
|
||||||
|
min = Math.ceil(min);
|
||||||
|
max = Math.floor(max);
|
||||||
|
return Math.floor(Math.random() * (max - min)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClusterDnsEndPointDiscoverer.prototype._randomShuffle = function (arr, i, j) {
|
||||||
|
if (i >= j)
|
||||||
|
return;
|
||||||
|
for (var k = i; k <= j; ++k)
|
||||||
|
{
|
||||||
|
var index = rndNext(k, j + 1);
|
||||||
|
var tmp = arr[index];
|
||||||
|
arr[index] = arr[k];
|
||||||
|
arr[k] = tmp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = ClusterDnsEndPointDiscoverer;
|
@ -36,6 +36,7 @@ const TimerTickMessage = new messages.TimerTickMessage();
|
|||||||
const EmptyGuid = '00000000-0000-0000-0000-000000000000';
|
const EmptyGuid = '00000000-0000-0000-0000-000000000000';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @private
|
||||||
* @param {EventStoreNodeConnection} esConnection
|
* @param {EventStoreNodeConnection} esConnection
|
||||||
* @param {Object} settings
|
* @param {Object} settings
|
||||||
* @constructor
|
* @constructor
|
||||||
@ -165,7 +166,7 @@ EventStoreConnectionLogicHandler.prototype._startConnection = function(cb, endpo
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
EventStoreConnectionLogicHandler.prototype._closeConnection = function(reason, error) {
|
EventStoreConnectionLogicHandler.prototype._closeConnection = function(reason, error) {
|
||||||
if (this._state == ConnectionState.Closed) {
|
if (this._state === ConnectionState.Closed) {
|
||||||
this._logDebug("CloseConnection IGNORED because is ESConnection is CLOSED, reason %s, error %s.", reason, error ? error.stack : '');
|
this._logDebug("CloseConnection IGNORED because is ESConnection is CLOSED, reason %s, error %s.", reason, error ? error.stack : '');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -189,7 +190,7 @@ EventStoreConnectionLogicHandler.prototype._closeConnection = function(reason, e
|
|||||||
|
|
||||||
EventStoreConnectionLogicHandler.prototype._closeTcpConnection = function(reason) {
|
EventStoreConnectionLogicHandler.prototype._closeTcpConnection = function(reason) {
|
||||||
if (!this._connection) {
|
if (!this._connection) {
|
||||||
this._logDebug("CloseTcpConnection IGNORED because _connection == null");
|
this._logDebug("CloseTcpConnection IGNORED because _connection === null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,7 +321,7 @@ EventStoreConnectionLogicHandler.prototype._startPersistentSubscription = functi
|
|||||||
|
|
||||||
EventStoreConnectionLogicHandler.prototype._establishTcpConnection = function(endPoints) {
|
EventStoreConnectionLogicHandler.prototype._establishTcpConnection = function(endPoints) {
|
||||||
var endPoint = this._settings.useSslConnection ? endPoints.secureTcpEndPoint : endPoints.tcpEndPoint;
|
var endPoint = this._settings.useSslConnection ? endPoints.secureTcpEndPoint : endPoints.tcpEndPoint;
|
||||||
if (endPoint == null)
|
if (endPoint === null)
|
||||||
{
|
{
|
||||||
this._closeConnection("No end point to node specified.");
|
this._closeConnection("No end point to node specified.");
|
||||||
return;
|
return;
|
||||||
@ -328,8 +329,8 @@ EventStoreConnectionLogicHandler.prototype._establishTcpConnection = function(en
|
|||||||
|
|
||||||
this._logDebug("EstablishTcpConnection to [%j]", endPoint);
|
this._logDebug("EstablishTcpConnection to [%j]", endPoint);
|
||||||
|
|
||||||
if (this._state != ConnectionState.Connecting) return;
|
if (this._state !== ConnectionState.Connecting) return;
|
||||||
if (this._connectingPhase != ConnectingPhase.EndPointDiscovery) return;
|
if (this._connectingPhase !== ConnectingPhase.EndPointDiscovery) return;
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
this._connectingPhase = ConnectingPhase.ConnectionEstablishing;
|
this._connectingPhase = ConnectingPhase.ConnectionEstablishing;
|
||||||
@ -358,10 +359,10 @@ EventStoreConnectionLogicHandler.prototype._establishTcpConnection = function(en
|
|||||||
};
|
};
|
||||||
|
|
||||||
EventStoreConnectionLogicHandler.prototype._tcpConnectionEstablished = function(connection) {
|
EventStoreConnectionLogicHandler.prototype._tcpConnectionEstablished = function(connection) {
|
||||||
if (this._state != ConnectionState.Connecting || !this._connection.equals(connection) || connection.isClosed)
|
if (this._state !== ConnectionState.Connecting || !this._connection || !this._connection.equals(connection) || connection.isClosed)
|
||||||
{
|
{
|
||||||
this._logDebug("IGNORED (_state %s, _conn.Id %s, conn.Id %s, conn.closed %s): TCP connection to [%j, L%j] established.",
|
this._logDebug("IGNORED (_state %s, _conn.Id %s, conn.Id %s, conn.closed %s): TCP connection to [%j, L%j] established.",
|
||||||
this._state, this._connection == null ? EmptyGuid : this._connection.connectionId, connection.connectionId,
|
this._state, this._connection === null ? EmptyGuid : this._connection.connectionId, connection.connectionId,
|
||||||
connection.isClosed, connection.remoteEndPoint, connection.localEndPoint);
|
connection.isClosed, connection.remoteEndPoint, connection.localEndPoint);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -373,7 +374,7 @@ EventStoreConnectionLogicHandler.prototype._tcpConnectionEstablished = function(
|
|||||||
timeStamp: Date.now()
|
timeStamp: Date.now()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this._settings.defaultUserCredentials != null)
|
if (this._settings.defaultUserCredentials !== null)
|
||||||
{
|
{
|
||||||
this._connectingPhase = ConnectingPhase.Authentication;
|
this._connectingPhase = ConnectingPhase.Authentication;
|
||||||
|
|
||||||
@ -402,7 +403,7 @@ EventStoreConnectionLogicHandler.prototype._goToConnectedState = function() {
|
|||||||
|
|
||||||
this.emit('connected', this._connection.remoteEndPoint);
|
this.emit('connected', this._connection.remoteEndPoint);
|
||||||
|
|
||||||
if (Date.now() - this._lastTimeoutsTimeStamp >= this._settings.operationTimeoutCheckPeriod)
|
if ((Date.now() - this._lastTimeoutsTimeStamp) >= this._settings.operationTimeoutCheckPeriod)
|
||||||
{
|
{
|
||||||
this._operations.checkTimeoutsAndRetry(this._connection);
|
this._operations.checkTimeoutsAndRetry(this._connection);
|
||||||
this._subscriptions.checkTimeoutsAndRetry(this._connection);
|
this._subscriptions.checkTimeoutsAndRetry(this._connection);
|
||||||
@ -411,19 +412,19 @@ EventStoreConnectionLogicHandler.prototype._goToConnectedState = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
EventStoreConnectionLogicHandler.prototype._tcpConnectionError = function(connection, error) {
|
EventStoreConnectionLogicHandler.prototype._tcpConnectionError = function(connection, error) {
|
||||||
if (this._connection != connection) return;
|
if (!this._connection || !this._connection.equals(connection)) return;
|
||||||
if (this._state == ConnectionState.Closed) return;
|
if (this._state === ConnectionState.Closed) return;
|
||||||
|
|
||||||
this._logDebug("TcpConnectionError connId %s, exc %s.", connection.connectionId, error);
|
this._logDebug("TcpConnectionError connId %s, exc %s.", connection.connectionId, error);
|
||||||
this._closeConnection("TCP connection error occurred.", error);
|
this._closeConnection("TCP connection error occurred.", error);
|
||||||
};
|
};
|
||||||
|
|
||||||
EventStoreConnectionLogicHandler.prototype._tcpConnectionClosed = function(connection, error) {
|
EventStoreConnectionLogicHandler.prototype._tcpConnectionClosed = function(connection, error) {
|
||||||
if (this._state == ConnectionState.Init) throw new Error();
|
if (this._state === ConnectionState.Init) throw new Error();
|
||||||
if (this._state == ConnectionState.Closed || !this._connection.equals(connection))
|
if (this._state === ConnectionState.Closed || !this._connection || !this._connection.equals(connection))
|
||||||
{
|
{
|
||||||
this._logDebug("IGNORED (_state: %s, _conn.ID: %s, conn.ID: %s): TCP connection to [%j, L%j] closed.",
|
this._logDebug("IGNORED (_state: %s, _conn.ID: %s, conn.ID: %s): TCP connection to [%j, L%j] closed.",
|
||||||
this._state, this._connection == null ? EmptyGuid : this._connection.connectionId, connection.connectionId,
|
this._state, this._connection === null ? EmptyGuid : this._connection.connectionId, connection.connectionId,
|
||||||
connection.remoteEndPoint, connection.localEndPoint);
|
connection.remoteEndPoint, connection.localEndPoint);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -447,7 +448,7 @@ EventStoreConnectionLogicHandler.prototype._tcpConnectionClosed = function(conne
|
|||||||
};
|
};
|
||||||
|
|
||||||
EventStoreConnectionLogicHandler.prototype._handleTcpPackage = function(connection, pkg) {
|
EventStoreConnectionLogicHandler.prototype._handleTcpPackage = function(connection, pkg) {
|
||||||
if (!connection.equals(this._connection) || this._state == ConnectionState.Closed || this._state == ConnectionState.Init)
|
if (!connection.equals(this._connection) || this._state === ConnectionState.Closed || this._state === ConnectionState.Init)
|
||||||
{
|
{
|
||||||
this._logDebug("IGNORED: HandleTcpPackage connId %s, package %s, %s.",
|
this._logDebug("IGNORED: HandleTcpPackage connId %s, package %s, %s.",
|
||||||
connection.connectionId, TcpCommand.getName(pkg.command), pkg.correlationId);
|
connection.connectionId, TcpCommand.getName(pkg.command), pkg.correlationId);
|
||||||
@ -458,9 +459,9 @@ EventStoreConnectionLogicHandler.prototype._handleTcpPackage = function(connecti
|
|||||||
this._connection.connectionId, TcpCommand.getName(pkg.command), pkg.correlationId);
|
this._connection.connectionId, TcpCommand.getName(pkg.command), pkg.correlationId);
|
||||||
this._packageNumber += 1;
|
this._packageNumber += 1;
|
||||||
|
|
||||||
if (pkg.command == TcpCommand.HeartbeatResponseCommand)
|
if (pkg.command === TcpCommand.HeartbeatResponseCommand)
|
||||||
return;
|
return;
|
||||||
if (pkg.command == TcpCommand.HeartbeatRequestCommand)
|
if (pkg.command === TcpCommand.HeartbeatRequestCommand)
|
||||||
{
|
{
|
||||||
this._connection.enqueueSend(new TcpPackage(
|
this._connection.enqueueSend(new TcpPackage(
|
||||||
TcpCommand.HeartbeatResponseCommand,
|
TcpCommand.HeartbeatResponseCommand,
|
||||||
@ -469,13 +470,13 @@ EventStoreConnectionLogicHandler.prototype._handleTcpPackage = function(connecti
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pkg.command == TcpCommand.Authenticated || pkg.command == TcpCommand.NotAuthenticated)
|
if (pkg.command === TcpCommand.Authenticated || pkg.command === TcpCommand.NotAuthenticated)
|
||||||
{
|
{
|
||||||
if (this._state == ConnectionState.Connecting
|
if (this._state === ConnectionState.Connecting
|
||||||
&& this._connectingPhase == ConnectingPhase.Authentication
|
&& this._connectingPhase === ConnectingPhase.Authentication
|
||||||
&& this._authInfo.correlationId == pkg.correlationId)
|
&& this._authInfo.correlationId === pkg.correlationId)
|
||||||
{
|
{
|
||||||
if (pkg.command == TcpCommand.NotAuthenticated)
|
if (pkg.command === TcpCommand.NotAuthenticated)
|
||||||
this.emit('authenticationFailed', "Not authenticated");
|
this.emit('authenticationFailed', "Not authenticated");
|
||||||
|
|
||||||
this._goToConnectedState();
|
this._goToConnectedState();
|
||||||
@ -483,7 +484,7 @@ EventStoreConnectionLogicHandler.prototype._handleTcpPackage = function(connecti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pkg.command == TcpCommand.BadRequest && pkg.correlationId == EmptyGuid)
|
if (pkg.command === TcpCommand.BadRequest && pkg.correlationId === EmptyGuid)
|
||||||
{
|
{
|
||||||
var message = "<no message>";
|
var message = "<no message>";
|
||||||
try {
|
try {
|
||||||
@ -515,7 +516,7 @@ EventStoreConnectionLogicHandler.prototype._handleTcpPackage = function(connecti
|
|||||||
default:
|
default:
|
||||||
throw new Error("Unknown InspectionDecision: " + result.decision);
|
throw new Error("Unknown InspectionDecision: " + result.decision);
|
||||||
}
|
}
|
||||||
if (this._state == ConnectionState.Connected)
|
if (this._state === ConnectionState.Connected)
|
||||||
this._operations.scheduleWaitingOperations(connection);
|
this._operations.scheduleWaitingOperations(connection);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -557,13 +558,13 @@ EventStoreConnectionLogicHandler.prototype._reconnectTo = function(endPoints) {
|
|||||||
var endPoint = this._settings.useSslConnection
|
var endPoint = this._settings.useSslConnection
|
||||||
? endPoints.secureTcpEndPoint
|
? endPoints.secureTcpEndPoint
|
||||||
: endPoints.tcpEndPoint;
|
: endPoints.tcpEndPoint;
|
||||||
if (endPoint == null)
|
if (endPoint === null)
|
||||||
{
|
{
|
||||||
this._closeConnection("No end point is specified while trying to reconnect.");
|
this._closeConnection("No end point is specified while trying to reconnect.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._state != ConnectionState.Connected || this._connection.remoteEndPoint == endPoint)
|
if (this._state !== ConnectionState.Connected || this._connection.remoteEndPoint === endPoint)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var msg = util.format("EventStoreConnection '%s': going to reconnect to [%j]. Current endpoint: [%j, L%j].",
|
var msg = util.format("EventStoreConnection '%s': going to reconnect to [%j]. Current endpoint: [%j, L%j].",
|
||||||
@ -581,7 +582,7 @@ EventStoreConnectionLogicHandler.prototype._timerTick = function() {
|
|||||||
{
|
{
|
||||||
case ConnectionState.Init: break;
|
case ConnectionState.Init: break;
|
||||||
case ConnectionState.Connecting:
|
case ConnectionState.Connecting:
|
||||||
if (this._connectingPhase == ConnectingPhase.Reconnecting && Date.now() - this._reconnInfo.timeStamp >= this._settings.reconnectionDelay)
|
if (this._connectingPhase === ConnectingPhase.Reconnecting && (Date.now() - this._reconnInfo.timeStamp) >= this._settings.reconnectionDelay)
|
||||||
{
|
{
|
||||||
this._logDebug("TimerTick checking reconnection...");
|
this._logDebug("TimerTick checking reconnection...");
|
||||||
|
|
||||||
@ -594,17 +595,17 @@ EventStoreConnectionLogicHandler.prototype._timerTick = function() {
|
|||||||
this._discoverEndpoint(null);
|
this._discoverEndpoint(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (this._connectingPhase == ConnectingPhase.Authentication && Date.now() - this._authInfo.timeStamp >= this._settings.operationTimeout)
|
else if (this._connectingPhase === ConnectingPhase.Authentication && (Date.now() - this._authInfo.timeStamp) >= this._settings.operationTimeout)
|
||||||
{
|
{
|
||||||
this.emit('authenticationFailed', "Authentication timed out.");
|
this.emit('authenticationFailed', "Authentication timed out.");
|
||||||
this._goToConnectedState();
|
this._goToConnectedState();
|
||||||
}
|
}
|
||||||
else if (this._connectingPhase > ConnectingPhase.ConnectionEstablishing)
|
else if (this._connectingPhase === ConnectingPhase.Authentication || this._connectingPhase === ConnectingPhase.Connected)
|
||||||
this._manageHeartbeats();
|
this._manageHeartbeats();
|
||||||
break;
|
break;
|
||||||
case ConnectionState.Connected:
|
case ConnectionState.Connected:
|
||||||
// operations timeouts are checked only if connection is established and check period time passed
|
// operations timeouts are checked only if connection is established and check period time passed
|
||||||
if (Date.now() - this._lastTimeoutsTimeStamp >= this._settings.operationTimeoutCheckPeriod)
|
if ((Date.now() - this._lastTimeoutsTimeStamp) >= this._settings.operationTimeoutCheckPeriod)
|
||||||
{
|
{
|
||||||
// On mono even impossible connection first says that it is established
|
// On mono even impossible connection first says that it is established
|
||||||
// so clearing of reconnection count on ConnectionEstablished event causes infinite reconnections.
|
// so clearing of reconnection count on ConnectionEstablished event causes infinite reconnections.
|
||||||
@ -624,14 +625,14 @@ EventStoreConnectionLogicHandler.prototype._timerTick = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
EventStoreConnectionLogicHandler.prototype._manageHeartbeats = function() {
|
EventStoreConnectionLogicHandler.prototype._manageHeartbeats = function() {
|
||||||
if (this._connection == null) throw new Error();
|
if (this._connection === null) return;
|
||||||
|
|
||||||
var timeout = this._heartbeatInfo.isIntervalStage ? this._settings.heartbeatInterval : this._settings.heartbeatTimeout;
|
var timeout = this._heartbeatInfo.isIntervalStage ? this._settings.heartbeatInterval : this._settings.heartbeatTimeout;
|
||||||
if (Date.now() - this._heartbeatInfo.timeStamp < timeout)
|
if ((Date.now() - this._heartbeatInfo.timeStamp) < timeout)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var packageNumber = this._packageNumber;
|
var packageNumber = this._packageNumber;
|
||||||
if (this._heartbeatInfo.lastPackageNumber != packageNumber)
|
if (this._heartbeatInfo.lastPackageNumber !== packageNumber)
|
||||||
{
|
{
|
||||||
this._heartbeatInfo = {lastPackageNumber: packageNumber, isIntervalStage: true, timeStamp: Date.now()};
|
this._heartbeatInfo = {lastPackageNumber: packageNumber, isIntervalStage: true, timeStamp: Date.now()};
|
||||||
return;
|
return;
|
||||||
|
@ -41,6 +41,7 @@ function StartSubscriptionMessage(
|
|||||||
util.inherits(StartSubscriptionMessage, Message);
|
util.inherits(StartSubscriptionMessage, Message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @private
|
||||||
* @constructor
|
* @constructor
|
||||||
* @property {object} endPoints
|
* @property {object} endPoints
|
||||||
* @property {object} endPoints.secureTcpEndPoint
|
* @property {object} endPoints.secureTcpEndPoint
|
||||||
|
@ -5,6 +5,7 @@ var Hash = require('../common/hash');
|
|||||||
var TcpCommand = require('../systemData/tcpCommand');
|
var TcpCommand = require('../systemData/tcpCommand');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @private
|
||||||
* @param {string} connectionName
|
* @param {string} connectionName
|
||||||
* @param {object} settings
|
* @param {object} settings
|
||||||
* @constructor
|
* @constructor
|
||||||
@ -29,17 +30,20 @@ OperationsManager.prototype.getActiveOperation = function(correlationId) {
|
|||||||
return this._activeOperations.get(correlationId);
|
return this._activeOperations.get(correlationId);
|
||||||
};
|
};
|
||||||
|
|
||||||
OperationsManager.prototype.cleanUp = function() {
|
function cleanUpError(connName, state, operation) {
|
||||||
var connectionClosedError = new Error(util.format("Connection '%s' was closed.", this._connectionName));
|
return new Error(util.format("Connection '%s' was closed. %s %s.", connName, state, operation.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
OperationsManager.prototype.cleanUp = function() {
|
||||||
|
var self = this;
|
||||||
this._activeOperations.forEach(function(correlationId, operation){
|
this._activeOperations.forEach(function(correlationId, operation){
|
||||||
operation.operation.fail(connectionClosedError);
|
operation.operation.fail(cleanUpError(self._connectionName, 'Active', operation));
|
||||||
});
|
});
|
||||||
this._waitingOperations.forEach(function(operation) {
|
this._waitingOperations.forEach(function(operation) {
|
||||||
operation.operation.fail(connectionClosedError);
|
operation.operation.fail(cleanUpError(self._connectionName, 'Waiting', operation));
|
||||||
});
|
});
|
||||||
this._retryPendingOperations.forEach(function(operation) {
|
this._retryPendingOperations.forEach(function(operation) {
|
||||||
operation.operation.fail(connectionClosedError);
|
operation.operation.fail(cleanUpError(self._connectionName, 'Pending', operation));
|
||||||
});
|
});
|
||||||
|
|
||||||
this._activeOperations.clear();
|
this._activeOperations.clear();
|
||||||
@ -55,7 +59,7 @@ OperationsManager.prototype.checkTimeoutsAndRetry = function(connection) {
|
|||||||
var removeOperations = [];
|
var removeOperations = [];
|
||||||
var self = this;
|
var self = this;
|
||||||
this._activeOperations.forEach(function(correlationId, operation) {
|
this._activeOperations.forEach(function(correlationId, operation) {
|
||||||
if (operation.connectionId != connection.connectionId)
|
if (operation.connectionId !== connection.connectionId)
|
||||||
{
|
{
|
||||||
retryOperations.push(operation);
|
retryOperations.push(operation);
|
||||||
}
|
}
|
||||||
@ -120,7 +124,7 @@ OperationsManager.prototype.scheduleOperationRetry = function(operation) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
OperationsManager.prototype.removeOperation = function(operation) {
|
OperationsManager.prototype.removeOperation = function(operation) {
|
||||||
this._activeOperations.remove(operation.connectionId);
|
this._activeOperations.remove(operation.correlationId);
|
||||||
this._logDebug("RemoveOperation SUCCEEDED for %s.", operation);
|
this._logDebug("RemoveOperation SUCCEEDED for %s.", operation);
|
||||||
this._totalOperationCount = this._activeOperations.length + this._waitingOperations.length;
|
this._totalOperationCount = this._activeOperations.length + this._waitingOperations.length;
|
||||||
return true;
|
return true;
|
||||||
|
@ -13,8 +13,14 @@ function SimpleQueuedHandler() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SimpleQueuedHandler.prototype.registerHandler = function(type, handler) {
|
SimpleQueuedHandler.prototype.registerHandler = function(type, handler) {
|
||||||
type = typeName(type);
|
var typeId = typeName(type);
|
||||||
this._handlers[type] = handler;
|
this._handlers[typeId] = function (msg) {
|
||||||
|
try {
|
||||||
|
handler(msg);
|
||||||
|
} catch(e) {
|
||||||
|
console.log('ERROR: ', e.stack);
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
SimpleQueuedHandler.prototype.enqueueMessage = function(msg) {
|
SimpleQueuedHandler.prototype.enqueueMessage = function(msg) {
|
||||||
@ -28,10 +34,10 @@ SimpleQueuedHandler.prototype.enqueueMessage = function(msg) {
|
|||||||
SimpleQueuedHandler.prototype._processQueue = function() {
|
SimpleQueuedHandler.prototype._processQueue = function() {
|
||||||
var message = this._messages.shift();
|
var message = this._messages.shift();
|
||||||
while(message) {
|
while(message) {
|
||||||
var type = typeName(message);
|
var typeId = typeName(message);
|
||||||
var handler = this._handlers[type];
|
var handler = this._handlers[typeId];
|
||||||
if (!handler)
|
if (!handler)
|
||||||
throw new Error("No handler registered for message " + type);
|
throw new Error("No handler registered for message " + typeId);
|
||||||
setImmediate(handler, message);
|
setImmediate(handler, message);
|
||||||
message = this._messages.shift();
|
message = this._messages.shift();
|
||||||
}
|
}
|
||||||
|
@ -38,11 +38,11 @@ SubscriptionsManager.prototype.cleanUp = function() {
|
|||||||
this._retryPendingSubscriptions = [];
|
this._retryPendingSubscriptions = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
SubscriptionsManager.prototype.purgeSubscribedAndDroppedSubscriptions = function() {
|
SubscriptionsManager.prototype.purgeSubscribedAndDroppedSubscriptions = function(connectionId) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var subscriptionsToRemove = [];
|
var subscriptionsToRemove = [];
|
||||||
this._activeSubscriptions.forEach(function(_, subscription) {
|
this._activeSubscriptions.forEach(function(_, subscription) {
|
||||||
if (subscription.isSubscribed && subscription.connectionId == connectionId) {
|
if (subscription.isSubscribed && subscription.connectionId === connectionId) {
|
||||||
subscription.operation.connectionClosed();
|
subscription.operation.connectionClosed();
|
||||||
subscriptionsToRemove.push(subscription);
|
subscriptionsToRemove.push(subscription);
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ SubscriptionsManager.prototype.checkTimeoutsAndRetry = function(connection) {
|
|||||||
var removeSubscriptions = [];
|
var removeSubscriptions = [];
|
||||||
this._activeSubscriptions.forEach(function(_, subscription) {
|
this._activeSubscriptions.forEach(function(_, subscription) {
|
||||||
if (subscription.isSubscribed) return;
|
if (subscription.isSubscribed) return;
|
||||||
if (subscription.connectionId != connection.connectionId)
|
if (subscription.connectionId !== connection.connectionId)
|
||||||
{
|
{
|
||||||
retrySubscriptions.push(subscription);
|
retrySubscriptions.push(subscription);
|
||||||
}
|
}
|
||||||
@ -165,7 +165,7 @@ SubscriptionsManager.prototype._logDebug = function(message) {
|
|||||||
if (!this._settings.verboseLogging) return;
|
if (!this._settings.verboseLogging) return;
|
||||||
|
|
||||||
var parameters = Array.prototype.slice.call(arguments, 1);
|
var parameters = Array.prototype.slice.call(arguments, 1);
|
||||||
this._settings.log.debug("EventStoreConnection '%s': %s.", this._connectionName, parameters.length == 0 ? message : util.format(message, parameters));
|
this._settings.log.debug("EventStoreConnection '%s': %s.", this._connectionName, parameters.length === 0 ? message : util.format(message, parameters));
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = SubscriptionsManager;
|
module.exports = SubscriptionsManager;
|
@ -1,11 +1,21 @@
|
|||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
var Long = require('long');
|
||||||
|
|
||||||
function AccessDeniedError(action, stream) {
|
function AccessDeniedError(action, streamOrTransactionId) {
|
||||||
Error.captureStackTrace(this, this.constructor);
|
Error.captureStackTrace(this, this.constructor);
|
||||||
this.name = this.constructor.name;
|
this.name = this.constructor.name;
|
||||||
this.message = util.format("%s access denied for stream '%s'.", action, stream);
|
|
||||||
this.action = action;
|
this.action = action;
|
||||||
this.stream = stream;
|
if (typeof streamOrTransactionId === 'string') {
|
||||||
|
this.message = util.format("%s access denied for stream '%s'.", action, streamOrTransactionId);
|
||||||
|
this.stream = streamOrTransactionId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Long.isLong(streamOrTransactionId)) {
|
||||||
|
this.message = util.format("%s access denied for transaction %s.", action, streamOrTransactionId);
|
||||||
|
this.transactionId = streamOrTransactionId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new TypeError("second argument must be a stream name or transaction Id.");
|
||||||
}
|
}
|
||||||
util.inherits(AccessDeniedError, Error);
|
util.inherits(AccessDeniedError, Error);
|
||||||
|
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
var Long = require('long');
|
||||||
|
|
||||||
function StreamDeletedError(stream) {
|
function StreamDeletedError(streamOrTransactionId) {
|
||||||
Error.captureStackTrace(this, this.constructor);
|
Error.captureStackTrace(this, this.constructor);
|
||||||
this.name = this.constructor.name;
|
this.name = this.constructor.name;
|
||||||
this.message = util.format("Event stream '%s' is deleted.", stream);
|
if (typeof streamOrTransactionId === 'string') {
|
||||||
this.stream = stream;
|
this.message = util.format("Event stream '%s' is deleted.", streamOrTransactionId);
|
||||||
|
this.stream = streamOrTransactionId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Long.isLong(streamOrTransactionId)) {
|
||||||
|
this.message = util.format("Stream is deleted for transaction %s.", streamOrTransactionId);
|
||||||
|
this.transactionId = streamOrTransactionId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new TypeError("second argument must be a stream name or transaction Id.");
|
||||||
}
|
}
|
||||||
util.inherits(StreamDeletedError, Error);
|
util.inherits(StreamDeletedError, Error);
|
||||||
|
|
||||||
|
@ -1,12 +1,22 @@
|
|||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
var Long = require('long');
|
||||||
|
|
||||||
function WrongExpectedVersionError(action, stream, expectedVersion) {
|
function WrongExpectedVersionError(action, streamOrTransactionId, expectedVersion) {
|
||||||
Error.captureStackTrace(this, this.constructor);
|
Error.captureStackTrace(this, this.constructor);
|
||||||
this.name = this.constructor.name;
|
this.name = this.constructor.name;
|
||||||
this.message = util.format("%s failed due to WrongExpectedVersion. Stream: %s Expected version: %d.", action, stream, expectedVersion);
|
|
||||||
this.action = action;
|
this.action = action;
|
||||||
this.stream = stream;
|
if (typeof streamOrTransactionId === 'string') {
|
||||||
this.expectedVersion = expectedVersion;
|
this.message = util.format("%s failed due to WrongExpectedVersion. Stream: %s Expected version: %d.", action, streamOrTransactionId, expectedVersion);
|
||||||
|
this.stream = streamOrTransactionId;
|
||||||
|
this.expectedVersion = expectedVersion;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Long.isLong(streamOrTransactionId)) {
|
||||||
|
this.message = util.format("%s transaction failed due to WrongExpectedVersion. Transaction Id: %s.", action, streamOrTransactionId);
|
||||||
|
this.transactionId = streamOrTransactionId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new TypeError("second argument must be a stream name or a transaction Id.");
|
||||||
}
|
}
|
||||||
util.inherits(WrongExpectedVersionError, Error);
|
util.inherits(WrongExpectedVersionError, Error);
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ function isValidId(id) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an EventData
|
* Create an EventData
|
||||||
|
* @private
|
||||||
* @param {string} eventId
|
* @param {string} eventId
|
||||||
* @param {string} type
|
* @param {string} type
|
||||||
* @param {boolean} [isJson]
|
* @param {boolean} [isJson]
|
||||||
|
@ -46,8 +46,6 @@ EventStoreAllCatchUpSubscription.prototype._readEventsTill = function(
|
|||||||
var done = lastCommitPosition === null
|
var done = lastCommitPosition === null
|
||||||
? slice.isEndOfStream
|
? slice.isEndOfStream
|
||||||
: slice.nextPosition.compareTo(new results.Position(lastCommitPosition, lastCommitPosition)) >= 0;
|
: slice.nextPosition.compareTo(new results.Position(lastCommitPosition, lastCommitPosition)) >= 0;
|
||||||
if (!done && slice.isEndOfStream)
|
|
||||||
return Promise.resolve(done).delay(10);
|
|
||||||
return Promise.resolve(done);
|
return Promise.resolve(done);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
var EventStoreNodeConnection = require('./eventStoreNodeConnection');
|
var EventStoreNodeConnection = require('./eventStoreNodeConnection');
|
||||||
var StaticEndpointDiscoverer = require('./core/staticEndpointDiscoverer');
|
var StaticEndpointDiscoverer = require('./core/staticEndpointDiscoverer');
|
||||||
|
var ClusterDnsEndPointDiscoverer = require('./core/clusterDnsEndPointDiscoverer');
|
||||||
var NoopLogger = require('./common/log/noopLogger');
|
var NoopLogger = require('./common/log/noopLogger');
|
||||||
|
var ensure = require('./common/utils/ensure');
|
||||||
|
|
||||||
var defaultConnectionSettings = {
|
var defaultConnectionSettings = {
|
||||||
log: new NoopLogger(),
|
log: new NoopLogger(),
|
||||||
@ -25,7 +27,13 @@ var defaultConnectionSettings = {
|
|||||||
failOnNoServerResponse: false,
|
failOnNoServerResponse: false,
|
||||||
heartbeatInterval: 750,
|
heartbeatInterval: 750,
|
||||||
heartbeatTimeout: 1500,
|
heartbeatTimeout: 1500,
|
||||||
clientConnectionTimeout: 1000
|
clientConnectionTimeout: 1000,
|
||||||
|
|
||||||
|
// Cluster Settings
|
||||||
|
clusterDns: '',
|
||||||
|
maxDiscoverAttempts: 10,
|
||||||
|
externalGossipPort: 0,
|
||||||
|
gossipTimeout: 1000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -40,16 +48,86 @@ function merge(a,b) {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createFromTcpEndpoint(settings, tcpEndpoint, connectionName) {
|
||||||
|
if (!tcpEndpoint.port || !tcpEndpoint.host) throw new TypeError('endPoint object must have host and port properties.');
|
||||||
|
var mergedSettings = merge(defaultConnectionSettings, settings || {});
|
||||||
|
var endpointDiscoverer = new StaticEndpointDiscoverer(tcpEndpoint, settings.useSslConnection);
|
||||||
|
return new EventStoreNodeConnection(mergedSettings, null, endpointDiscoverer, connectionName || null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFromStringEndpoint(settings, endPoint, connectionName) {
|
||||||
|
var m = endPoint.match(/^(tcp|discover):\/\/([^:]+):?(\d+)?$/);
|
||||||
|
if (!m) throw new Error('endPoint string must be tcp://hostname[:port] or discover://dns[:port]');
|
||||||
|
var scheme = m[1];
|
||||||
|
var host = m[2] || null;
|
||||||
|
var port = m[3] ? parseInt(m[3]) : null;
|
||||||
|
if (scheme === 'tcp') {
|
||||||
|
var tcpEndpoint = {
|
||||||
|
host: host,
|
||||||
|
port: port || 1113
|
||||||
|
};
|
||||||
|
return createFromTcpEndpoint(settings, tcpEndpoint, connectionName);
|
||||||
|
}
|
||||||
|
if (scheme === 'discover') {
|
||||||
|
return createFromClusterDns(settings, host, port || 2113, connectionName);
|
||||||
|
}
|
||||||
|
throw new Error('Invalid scheme for endPoint: ' + scheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFromClusterDns(connectionSettings, clusterDns, externalGossipPort, connectionName) {
|
||||||
|
ensure.notNull(connectionSettings, "connectionSettings");
|
||||||
|
ensure.notNull(clusterDns, "clusterDns");
|
||||||
|
var mergedSettings = merge(defaultConnectionSettings, connectionSettings || {});
|
||||||
|
var clusterSettings = {
|
||||||
|
clusterDns: clusterDns,
|
||||||
|
gossipSeeds: null,
|
||||||
|
externalGossipPort: externalGossipPort,
|
||||||
|
maxDiscoverAttempts: mergedSettings.maxDiscoverAttempts,
|
||||||
|
gossipTimeout: mergedSettings.gossipTimeout
|
||||||
|
};
|
||||||
|
var endPointDiscoverer = new ClusterDnsEndPointDiscoverer(mergedSettings.log,
|
||||||
|
clusterSettings.clusterDns,
|
||||||
|
clusterSettings.maxDiscoverAttempts,
|
||||||
|
clusterSettings.externalGossipPort,
|
||||||
|
clusterSettings.gossipSeeds,
|
||||||
|
clusterSettings.gossipTimeout
|
||||||
|
);
|
||||||
|
return new EventStoreNodeConnection(mergedSettings, clusterSettings, endPointDiscoverer, connectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFromGossipSeeds(connectionSettings, gossipSeeds, connectionName) {
|
||||||
|
ensure.notNull(connectionSettings, "connectionSettings");
|
||||||
|
ensure.notNull(gossipSeeds, "gossipSeeds");
|
||||||
|
var mergedSettings = merge(defaultConnectionSettings, connectionSettings || {});
|
||||||
|
var clusterSettings = {
|
||||||
|
clusterDns: '',
|
||||||
|
gossipSeeds: gossipSeeds,
|
||||||
|
externalGossipPort: 0,
|
||||||
|
maxDiscoverAttempts: mergedSettings.maxDiscoverAttempts,
|
||||||
|
gossipTimeout: mergedSettings.gossipTimeout
|
||||||
|
};
|
||||||
|
var endPointDiscoverer = new ClusterDnsEndPointDiscoverer(mergedSettings.log,
|
||||||
|
clusterSettings.clusterDns,
|
||||||
|
clusterSettings.maxDiscoverAttempts,
|
||||||
|
clusterSettings.externalGossipPort,
|
||||||
|
clusterSettings.gossipSeeds,
|
||||||
|
clusterSettings.gossipTimeout
|
||||||
|
);
|
||||||
|
return new EventStoreNodeConnection(mergedSettings, clusterSettings, endPointDiscoverer, connectionName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an EventStore connection
|
* Create an EventStore connection
|
||||||
|
* @public
|
||||||
|
* @alias createConnection
|
||||||
* @param {object} settings
|
* @param {object} settings
|
||||||
* @param {object} tcpEndPoint
|
* @param {string|object|array} endPointOrGossipSeeds
|
||||||
* @param {string} [connectionName]
|
* @param {string} [connectionName]
|
||||||
* @returns {EventStoreNodeConnection}
|
* @returns {EventStoreNodeConnection}
|
||||||
*/
|
*/
|
||||||
module.exports.create = function(settings, tcpEndPoint, connectionName) {
|
module.exports.create = function(settings, endPointOrGossipSeeds, connectionName) {
|
||||||
//TODO: cluster connection
|
if (Array.isArray(endPointOrGossipSeeds)) return createFromGossipSeeds(settings, endPointOrGossipSeeds, connectionName);
|
||||||
var mergedSettings = merge(defaultConnectionSettings, settings || {});
|
if (typeof endPointOrGossipSeeds === 'object') return createFromTcpEndpoint(settings, endPointOrGossipSeeds, connectionName);
|
||||||
var endpointDiscoverer = new StaticEndpointDiscoverer(tcpEndPoint, settings.useSslConnection);
|
if (typeof endPointOrGossipSeeds === 'string') return createFromStringEndpoint(settings, endPointOrGossipSeeds, connectionName);
|
||||||
return new EventStoreNodeConnection(mergedSettings, endpointDiscoverer, connectionName || null);
|
throw new TypeError('endPointOrGossipSeeds must be an object, a string or an array.');
|
||||||
};
|
};
|
@ -33,15 +33,13 @@ var EventData = require('./eventData');
|
|||||||
const MaxReadSize = 4096;
|
const MaxReadSize = 4096;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param settings
|
* @protected
|
||||||
* @param endpointDiscoverer
|
|
||||||
* @param connectionName
|
|
||||||
* @constructor
|
* @constructor
|
||||||
* @property {string} connectionName
|
|
||||||
*/
|
*/
|
||||||
function EventStoreNodeConnection(settings, endpointDiscoverer, connectionName) {
|
function EventStoreNodeConnection(settings, clusterSettings, endpointDiscoverer, connectionName) {
|
||||||
this._connectionName = connectionName || ['ES-', uuid.v4()].join('');
|
this._connectionName = connectionName || ['ES-', uuid.v4()].join('');
|
||||||
this._settings = settings;
|
this._settings = settings;
|
||||||
|
this._clusterSettings = clusterSettings;
|
||||||
this._endpointDiscoverer = endpointDiscoverer;
|
this._endpointDiscoverer = endpointDiscoverer;
|
||||||
this._handler = new EventStoreConnectionLogicHandler(this, settings);
|
this._handler = new EventStoreConnectionLogicHandler(this, settings);
|
||||||
|
|
||||||
@ -71,6 +69,8 @@ Object.defineProperty(EventStoreNodeConnection.prototype, 'connectionName', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Start connection task
|
||||||
|
* @public
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
EventStoreNodeConnection.prototype.connect = function() {
|
EventStoreNodeConnection.prototype.connect = function() {
|
||||||
@ -85,12 +85,17 @@ EventStoreNodeConnection.prototype.connect = function() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close connection
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
EventStoreNodeConnection.prototype.close = function() {
|
EventStoreNodeConnection.prototype.close = function() {
|
||||||
this._handler.enqueueMessage(new messages.CloseConnectionMessage("Connection close requested by client.", null));
|
this._handler.enqueueMessage(new messages.CloseConnectionMessage("Connection close requested by client.", null));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a stream (async)
|
* Delete a stream (async)
|
||||||
|
* @public
|
||||||
* @param {string} stream
|
* @param {string} stream
|
||||||
* @param {number} expectedVersion
|
* @param {number} expectedVersion
|
||||||
* @param {boolean} [hardDelete]
|
* @param {boolean} [hardDelete]
|
||||||
@ -118,6 +123,7 @@ EventStoreNodeConnection.prototype.deleteStream = function(stream, expectedVersi
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Append events to a stream (async)
|
* Append events to a stream (async)
|
||||||
|
* @public
|
||||||
* @param {string} stream The name of the stream to which to append.
|
* @param {string} stream The name of the stream to which to append.
|
||||||
* @param {number} expectedVersion The version at which we currently expect the stream to be in order that an optimistic concurrency check can be performed.
|
* @param {number} expectedVersion The version at which we currently expect the stream to be in order that an optimistic concurrency check can be performed.
|
||||||
* @param {EventData[]|EventData} events The event(s) to append.
|
* @param {EventData[]|EventData} events The event(s) to append.
|
||||||
@ -146,13 +152,17 @@ EventStoreNodeConnection.prototype.appendToStream = function(stream, expectedVer
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a transaction (async)
|
* Start a transaction (async)
|
||||||
|
* @public
|
||||||
* @param {string} stream
|
* @param {string} stream
|
||||||
* @param {number} expectedVersion
|
* @param {number} expectedVersion
|
||||||
* @param {UserCredentials} [userCredentials]
|
* @param {UserCredentials} [userCredentials]
|
||||||
* @returns {Promise.<EventStoreTransaction>}
|
* @returns {Promise.<EventStoreTransaction>}
|
||||||
*/
|
*/
|
||||||
EventStoreNodeConnection.prototype.startTransaction = function(stream, expectedVersion, userCredentials) {
|
EventStoreNodeConnection.prototype.startTransaction = function(stream, expectedVersion, userCredentials) {
|
||||||
//TODO validations
|
ensure.notNullOrEmpty(stream, "stream");
|
||||||
|
ensure.isInteger(expectedVersion, "expectedVersion");
|
||||||
|
userCredentials = userCredentials || null;
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
function cb(err, result) {
|
function cb(err, result) {
|
||||||
@ -160,23 +170,29 @@ EventStoreNodeConnection.prototype.startTransaction = function(stream, expectedV
|
|||||||
resolve(result);
|
resolve(result);
|
||||||
}
|
}
|
||||||
var operation = new StartTransactionOperation(self._settings.log, cb, self._settings.requireMaster, stream,
|
var operation = new StartTransactionOperation(self._settings.log, cb, self._settings.requireMaster, stream,
|
||||||
expectedVersion, self, userCredentials || null);
|
expectedVersion, self, userCredentials);
|
||||||
self._enqueueOperation(operation);
|
self._enqueueOperation(operation);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Continue a transaction
|
* Continue a transaction
|
||||||
|
* @public
|
||||||
* @param {number} transactionId
|
* @param {number} transactionId
|
||||||
* @param {UserCredentials} userCredentials
|
* @param {UserCredentials} userCredentials
|
||||||
* @returns {EventStoreTransaction}
|
* @returns {EventStoreTransaction}
|
||||||
*/
|
*/
|
||||||
EventStoreNodeConnection.prototype.continueTransaction = function(transactionId, userCredentials) {
|
EventStoreNodeConnection.prototype.continueTransaction = function(transactionId, userCredentials) {
|
||||||
//TODO validations
|
ensure.nonNegative(transactionId, "transactionId");
|
||||||
|
|
||||||
return new EventStoreTransaction(transactionId, userCredentials, this);
|
return new EventStoreTransaction(transactionId, userCredentials, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
EventStoreNodeConnection.prototype.transactionalWrite = function(transaction, events, userCredentials) {
|
EventStoreNodeConnection.prototype.transactionalWrite = function(transaction, events, userCredentials) {
|
||||||
|
ensure.isTypeOf(EventStoreTransaction, transaction, "transaction");
|
||||||
|
ensure.isArrayOf(EventData, events, "events");
|
||||||
|
userCredentials = userCredentials || null;
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
function cb(err) {
|
function cb(err) {
|
||||||
@ -189,7 +205,16 @@ EventStoreNodeConnection.prototype.transactionalWrite = function(transaction, ev
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commit a transaction
|
||||||
|
* @public
|
||||||
|
* @param transaction
|
||||||
|
* @param userCredentials
|
||||||
|
* @returns {Promise.<WriteResult>}
|
||||||
|
*/
|
||||||
EventStoreNodeConnection.prototype.commitTransaction = function(transaction, userCredentials) {
|
EventStoreNodeConnection.prototype.commitTransaction = function(transaction, userCredentials) {
|
||||||
|
ensure.isTypeOf(EventStoreTransaction, transaction, "transaction");
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
function cb(err, result) {
|
function cb(err, result) {
|
||||||
@ -204,6 +229,7 @@ EventStoreNodeConnection.prototype.commitTransaction = function(transaction, use
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a single event (async)
|
* Read a single event (async)
|
||||||
|
* @public
|
||||||
* @param {string} stream
|
* @param {string} stream
|
||||||
* @param {number} eventNumber
|
* @param {number} eventNumber
|
||||||
* @param {boolean} [resolveLinkTos]
|
* @param {boolean} [resolveLinkTos]
|
||||||
@ -236,6 +262,7 @@ EventStoreNodeConnection.prototype.readEvent = function(stream, eventNumber, res
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reading a specific stream forwards (async)
|
* Reading a specific stream forwards (async)
|
||||||
|
* @public
|
||||||
* @param {string} stream
|
* @param {string} stream
|
||||||
* @param {number} start
|
* @param {number} start
|
||||||
* @param {number} count
|
* @param {number} count
|
||||||
@ -269,6 +296,7 @@ EventStoreNodeConnection.prototype.readStreamEventsForward = function(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reading a specific stream backwards (async)
|
* Reading a specific stream backwards (async)
|
||||||
|
* @public
|
||||||
* @param {string} stream
|
* @param {string} stream
|
||||||
* @param {number} start
|
* @param {number} start
|
||||||
* @param {number} count
|
* @param {number} count
|
||||||
@ -301,6 +329,7 @@ EventStoreNodeConnection.prototype.readStreamEventsBackward = function(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reading all events forwards (async)
|
* Reading all events forwards (async)
|
||||||
|
* @public
|
||||||
* @param {Position} position
|
* @param {Position} position
|
||||||
* @param {number} maxCount
|
* @param {number} maxCount
|
||||||
* @param {boolean} [resolveLinkTos]
|
* @param {boolean} [resolveLinkTos]
|
||||||
@ -331,6 +360,7 @@ EventStoreNodeConnection.prototype.readAllEventsForward = function(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reading all events backwards (async)
|
* Reading all events backwards (async)
|
||||||
|
* @public
|
||||||
* @param {Position} position
|
* @param {Position} position
|
||||||
* @param {number} maxCount
|
* @param {number} maxCount
|
||||||
* @param {boolean} [resolveLinkTos]
|
* @param {boolean} [resolveLinkTos]
|
||||||
@ -361,6 +391,7 @@ EventStoreNodeConnection.prototype.readAllEventsBackward = function(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribe to a stream (async)
|
* Subscribe to a stream (async)
|
||||||
|
* @public
|
||||||
* @param {!string} stream
|
* @param {!string} stream
|
||||||
* @param {!boolean} resolveLinkTos
|
* @param {!boolean} resolveLinkTos
|
||||||
* @param {function} eventAppeared
|
* @param {function} eventAppeared
|
||||||
@ -371,8 +402,10 @@ EventStoreNodeConnection.prototype.readAllEventsBackward = function(
|
|||||||
EventStoreNodeConnection.prototype.subscribeToStream = function(
|
EventStoreNodeConnection.prototype.subscribeToStream = function(
|
||||||
stream, resolveLinkTos, eventAppeared, subscriptionDropped, userCredentials
|
stream, resolveLinkTos, eventAppeared, subscriptionDropped, userCredentials
|
||||||
) {
|
) {
|
||||||
if (typeof stream !== 'string' || stream === '') throw new TypeError("stream must be a non-empty string.");
|
ensure.notNullOrEmpty(stream, "stream");
|
||||||
if (typeof eventAppeared !== 'function') throw new TypeError("eventAppeared must be a function.");
|
ensure.isTypeOf(Function, eventAppeared, "eventAppeared");
|
||||||
|
if (subscriptionDropped)
|
||||||
|
ensure.isTypeOf(Function, subscriptionDropped, "subscriptionDropped");
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
return new Promise(function(resolve,reject) {
|
return new Promise(function(resolve,reject) {
|
||||||
@ -388,6 +421,8 @@ EventStoreNodeConnection.prototype.subscribeToStream = function(
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Subscribe to a stream from position
|
||||||
|
* @public
|
||||||
* @param {!string} stream
|
* @param {!string} stream
|
||||||
* @param {?number} lastCheckpoint
|
* @param {?number} lastCheckpoint
|
||||||
* @param {!boolean} resolveLinkTos
|
* @param {!boolean} resolveLinkTos
|
||||||
@ -416,6 +451,7 @@ EventStoreNodeConnection.prototype.subscribeToStreamFrom = function(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribe to all (async)
|
* Subscribe to all (async)
|
||||||
|
* @public
|
||||||
* @param {!boolean} resolveLinkTos
|
* @param {!boolean} resolveLinkTos
|
||||||
* @param {!function} eventAppeared
|
* @param {!function} eventAppeared
|
||||||
* @param {function} [subscriptionDropped]
|
* @param {function} [subscriptionDropped]
|
||||||
@ -442,6 +478,7 @@ EventStoreNodeConnection.prototype.subscribeToAll = function(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribe to all from
|
* Subscribe to all from
|
||||||
|
* @public
|
||||||
* @param {?Position} lastCheckpoint
|
* @param {?Position} lastCheckpoint
|
||||||
* @param {!boolean} resolveLinkTos
|
* @param {!boolean} resolveLinkTos
|
||||||
* @param {!function} eventAppeared
|
* @param {!function} eventAppeared
|
||||||
@ -455,7 +492,10 @@ EventStoreNodeConnection.prototype.subscribeToAllFrom = function(
|
|||||||
lastCheckpoint, resolveLinkTos, eventAppeared, liveProcessingStarted, subscriptionDropped,
|
lastCheckpoint, resolveLinkTos, eventAppeared, liveProcessingStarted, subscriptionDropped,
|
||||||
userCredentials, readBatchSize
|
userCredentials, readBatchSize
|
||||||
) {
|
) {
|
||||||
if (typeof eventAppeared !== 'function') throw new TypeError("eventAppeared must be a function.");
|
ensure.isTypeOf(results.Position, lastCheckpoint, "lastCheckpoint", true);
|
||||||
|
ensure.isTypeOf(Function, eventAppeared, "eventAppeared", false);
|
||||||
|
if (liveProcessingStarted) ensure.isTypeOf(Function, liveProcessingStarted, "liveProcessingStarted", false);
|
||||||
|
if (subscriptionDropped) ensure.isTypeOf(Function, subscriptionDropped, "subscriptionDropped", false);
|
||||||
|
|
||||||
var catchUpSubscription =
|
var catchUpSubscription =
|
||||||
new EventStoreAllCatchUpSubscription(this, this._settings.log, lastCheckpoint, resolveLinkTos,
|
new EventStoreAllCatchUpSubscription(this, this._settings.log, lastCheckpoint, resolveLinkTos,
|
||||||
@ -467,6 +507,7 @@ EventStoreNodeConnection.prototype.subscribeToAllFrom = function(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribe to a persistent subscription
|
* Subscribe to a persistent subscription
|
||||||
|
* @public
|
||||||
* @param {string} stream
|
* @param {string} stream
|
||||||
* @param {string} groupName
|
* @param {string} groupName
|
||||||
* @param {function} eventAppeared
|
* @param {function} eventAppeared
|
||||||
@ -474,6 +515,7 @@ EventStoreNodeConnection.prototype.subscribeToAllFrom = function(
|
|||||||
* @param {UserCredentials} [userCredentials]
|
* @param {UserCredentials} [userCredentials]
|
||||||
* @param {number} [bufferSize]
|
* @param {number} [bufferSize]
|
||||||
* @param {boolean} [autoAck]
|
* @param {boolean} [autoAck]
|
||||||
|
* @return {EventStorePersistentSubscription}
|
||||||
*/
|
*/
|
||||||
EventStoreNodeConnection.prototype.connectToPersistentSubscription = function(
|
EventStoreNodeConnection.prototype.connectToPersistentSubscription = function(
|
||||||
stream, groupName, eventAppeared, subscriptionDropped, userCredentials, bufferSize, autoAck
|
stream, groupName, eventAppeared, subscriptionDropped, userCredentials, bufferSize, autoAck
|
||||||
@ -496,6 +538,8 @@ EventStoreNodeConnection.prototype.connectToPersistentSubscription = function(
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Create a persistent subscription
|
||||||
|
* @public
|
||||||
* @param {string} stream
|
* @param {string} stream
|
||||||
* @param {string} groupName
|
* @param {string} groupName
|
||||||
* @param {PersistentSubscriptionSettings} settings
|
* @param {PersistentSubscriptionSettings} settings
|
||||||
@ -519,6 +563,8 @@ EventStoreNodeConnection.prototype.createPersistentSubscription = function(strea
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Update a persistent subscription
|
||||||
|
* @public
|
||||||
* @param {string} stream
|
* @param {string} stream
|
||||||
* @param {string} groupName
|
* @param {string} groupName
|
||||||
* @param {string} settings
|
* @param {string} settings
|
||||||
@ -541,6 +587,8 @@ EventStoreNodeConnection.prototype.updatePersistentSubscription = function(strea
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Delete a persistent subscription
|
||||||
|
* @public
|
||||||
* @param {string} stream
|
* @param {string} stream
|
||||||
* @param {string} groupName
|
* @param {string} groupName
|
||||||
* @param {UserCredentials} [userCredentials]
|
* @param {UserCredentials} [userCredentials]
|
||||||
@ -560,6 +608,10 @@ EventStoreNodeConnection.prototype.deletePersistentSubscription = function(strea
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set stream metadata
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
EventStoreNodeConnection.prototype.setStreamMetadata = function() {
|
EventStoreNodeConnection.prototype.setStreamMetadata = function() {
|
||||||
//TODO: set stream metadata (non-raw)
|
//TODO: set stream metadata (non-raw)
|
||||||
throw new Error("Not implemented.");
|
throw new Error("Not implemented.");
|
||||||
@ -567,6 +619,7 @@ EventStoreNodeConnection.prototype.setStreamMetadata = function() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set stream metadata with raw object (async)
|
* Set stream metadata with raw object (async)
|
||||||
|
* @public
|
||||||
* @param {string} stream
|
* @param {string} stream
|
||||||
* @param {number} expectedMetastreamVersion
|
* @param {number} expectedMetastreamVersion
|
||||||
* @param {object} metadata
|
* @param {object} metadata
|
||||||
@ -594,6 +647,12 @@ EventStoreNodeConnection.prototype.setStreamMetadataRaw = function(
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get stream metadata
|
||||||
|
* @private
|
||||||
|
* @param stream
|
||||||
|
* @param userCredentials
|
||||||
|
*/
|
||||||
EventStoreNodeConnection.prototype.getStreamMetadata = function(stream, userCredentials) {
|
EventStoreNodeConnection.prototype.getStreamMetadata = function(stream, userCredentials) {
|
||||||
//TODO: get stream metadata (non-raw)
|
//TODO: get stream metadata (non-raw)
|
||||||
throw new Error("Not implemented.");
|
throw new Error("Not implemented.");
|
||||||
@ -601,6 +660,7 @@ EventStoreNodeConnection.prototype.getStreamMetadata = function(stream, userCred
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get stream metadata as raw object (async)
|
* Get stream metadata as raw object (async)
|
||||||
|
* @public
|
||||||
* @param {string} stream
|
* @param {string} stream
|
||||||
* @param {UserCredentials} [userCredentials]
|
* @param {UserCredentials} [userCredentials]
|
||||||
* @returns {Promise.<RawStreamMetadataResult>}
|
* @returns {Promise.<RawStreamMetadataResult>}
|
||||||
@ -626,6 +686,10 @@ EventStoreNodeConnection.prototype.getStreamMetadataRaw = function(stream, userC
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set system settings
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
EventStoreNodeConnection.prototype.setSystemSettings = function() {
|
EventStoreNodeConnection.prototype.setSystemSettings = function() {
|
||||||
//TODO: set system settings
|
//TODO: set system settings
|
||||||
throw new Error("Not implemented.");
|
throw new Error("Not implemented.");
|
||||||
|
@ -38,6 +38,7 @@ EventStorePersistentSubscriptionBase.prototype.start = function() {
|
|||||||
this._startSubscription(this._subscriptionId, this._streamId, this._bufferSize, this._userCredentials,
|
this._startSubscription(this._subscriptionId, this._streamId, this._bufferSize, this._userCredentials,
|
||||||
this._onEventAppeared.bind(this), this._onSubscriptionDropped.bind(this), this._settings)
|
this._onEventAppeared.bind(this), this._onSubscriptionDropped.bind(this), this._settings)
|
||||||
.then(function(subscription) {
|
.then(function(subscription) {
|
||||||
|
console.log('Subscription started.');
|
||||||
self._subscription = subscription;
|
self._subscription = subscription;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -145,7 +146,7 @@ EventStorePersistentSubscriptionBase.prototype._processQueue = function() {
|
|||||||
e = this._queue.shift();
|
e = this._queue.shift();
|
||||||
}
|
}
|
||||||
this._isProcessing = false;
|
this._isProcessing = false;
|
||||||
//} while (_queue.Count > 0 && Interlocked.CompareExchange(ref _isProcessing, 1, 0) == 0);
|
//} while (_queue.Count > 0 && Interlocked.CompareExchange(ref _isProcessing, 1, 0) === 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
EventStorePersistentSubscriptionBase.prototype._dropSubscription = function(reason, error) {
|
EventStorePersistentSubscriptionBase.prototype._dropSubscription = function(reason, error) {
|
||||||
|
@ -33,13 +33,13 @@ EventStoreTransaction.prototype.commit = function() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Write events (async)
|
* Write events (async)
|
||||||
* @param {Array.<EventData>} events
|
* @param {EventData|EventData[]} eventOrEvents
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
EventStoreTransaction.prototype.write = function(events) {
|
EventStoreTransaction.prototype.write = function(eventOrEvents) {
|
||||||
if (this._isRolledBack) throw new Error("can't write to a rolledback transaction");
|
if (this._isRolledBack) throw new Error("can't write to a rolledback transaction");
|
||||||
if (this._isCommitted) throw new Error("Transaction is already committed");
|
if (this._isCommitted) throw new Error("Transaction is already committed");
|
||||||
if (!Array.isArray(events)) throw new Error("events must be an array.");
|
var events = Array.isArray(eventOrEvents) ? eventOrEvents : [eventOrEvents];
|
||||||
return this._connection.transactionalWrite(this, events);
|
return this._connection.transactionalWrite(this, events);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
13
src/gossipSeed.js
Normal file
13
src/gossipSeed.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
module.exports = function GossipSeed(endPoint, hostName) {
|
||||||
|
if (typeof endPoint !== 'object' || !endPoint.host || !endPoint.port) throw new TypeError('endPoint must be have host and port properties.');
|
||||||
|
Object.defineProperties(this, {
|
||||||
|
endPoint: {
|
||||||
|
enumerable: true,
|
||||||
|
value: endPoint
|
||||||
|
},
|
||||||
|
hostName: {
|
||||||
|
enumerable: true,
|
||||||
|
value: hostName
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@ -21,6 +21,8 @@ function PersistentSubscriptionSettings(
|
|||||||
this.namedConsumerStrategy = namedConsumerStrategy;
|
this.namedConsumerStrategy = namedConsumerStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.create = function() {
|
PersistentSubscriptionSettings.create = function() {
|
||||||
return new PersistentSubscriptionSettings(false, -1, false, 30000, 500, 500, 10, 20, 2000, 10, 1000, 0, SystemConsumerStrategies.RoundRobin);
|
return new PersistentSubscriptionSettings(false, -1, false, 30000, 500, 500, 10, 20, 2000, 10, 1000, 0, SystemConsumerStrategies.RoundRobin);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = PersistentSubscriptionSettings;
|
@ -4,6 +4,7 @@ var Long = require('long');
|
|||||||
var ensure = require('./common/utils/ensure');
|
var ensure = require('./common/utils/ensure');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @public
|
||||||
* @param {!number|!Long} commitPosition
|
* @param {!number|!Long} commitPosition
|
||||||
* @param {!number|!Long} preparePosition
|
* @param {!number|!Long} preparePosition
|
||||||
* @constructor
|
* @constructor
|
||||||
@ -65,11 +66,11 @@ function RecordedEvent(ev) {
|
|||||||
eventNumber: {enumerable: true, value: ev.event_number},
|
eventNumber: {enumerable: true, value: ev.event_number},
|
||||||
eventType: {enumerable: true, value: ev.event_type},
|
eventType: {enumerable: true, value: ev.event_type},
|
||||||
//Javascript doesn't have .Net precision for time, so we use created_epoch for created
|
//Javascript doesn't have .Net precision for time, so we use created_epoch for created
|
||||||
created: {enumerable: true, value: new Date(ev.created_epoch ? ev.created_epoch.toInt() : 0)},
|
created: {enumerable: true, value: new Date(ev.created_epoch ? ev.created_epoch.toNumber() : 0)},
|
||||||
createdEpoch: {enumerable: true, value: ev.created_epoch ? ev.created_epoch.toInt() : 0},
|
createdEpoch: {enumerable: true, value: ev.created_epoch ? ev.created_epoch.toNumber() : 0},
|
||||||
data: {enumerable: true, value: ev.data ? ev.data.toBuffer() : new Buffer(0)},
|
data: {enumerable: true, value: ev.data ? ev.data.toBuffer() : new Buffer(0)},
|
||||||
metadata: {enumerable: true, value: ev.metadata ? ev.metadata.toBuffer() : new Buffer(0)},
|
metadata: {enumerable: true, value: ev.metadata ? ev.metadata.toBuffer() : new Buffer(0)},
|
||||||
isJson: {enumerable: true, value: ev.data_content_type == 1}
|
isJson: {enumerable: true, value: ev.data_content_type === 1}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ TcpPackage.fromBufferSegment = function(data) {
|
|||||||
|
|
||||||
var headerSize = MandatorySize;
|
var headerSize = MandatorySize;
|
||||||
var login = null, pass = null;
|
var login = null, pass = null;
|
||||||
if ((flags & TcpFlags.Authenticated) != 0)
|
if ((flags & TcpFlags.Authenticated) !== 0)
|
||||||
{
|
{
|
||||||
var loginLen = data.buffer[data.offset + AuthOffset];
|
var loginLen = data.buffer[data.offset + AuthOffset];
|
||||||
if (AuthOffset + 1 + loginLen + 1 >= data.count)
|
if (AuthOffset + 1 + loginLen + 1 >= data.count)
|
||||||
@ -48,7 +48,7 @@ TcpPackage.fromBufferSegment = function(data) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TcpPackage.prototype.asBuffer = function() {
|
TcpPackage.prototype.asBuffer = function() {
|
||||||
if ((this.flags & TcpFlags.Authenticated) != 0) {
|
if ((this.flags & TcpFlags.Authenticated) !== 0) {
|
||||||
var loginBytes = new Buffer(this.login);
|
var loginBytes = new Buffer(this.login);
|
||||||
if (loginBytes.length > 255) throw new Error("Login serialized length should be less than 256 bytes.");
|
if (loginBytes.length > 255) throw new Error("Login serialized length should be less than 256 bytes.");
|
||||||
var passwordBytes = new Buffer(this.password);
|
var passwordBytes = new Buffer(this.password);
|
||||||
|
@ -29,7 +29,7 @@ LengthPrefixMessageFramer.prototype._parse = function(bytes) {
|
|||||||
{
|
{
|
||||||
this._packageLength |= (buffer[i] << (this._headerBytes * 8)); // little-endian order
|
this._packageLength |= (buffer[i] << (this._headerBytes * 8)); // little-endian order
|
||||||
++this._headerBytes;
|
++this._headerBytes;
|
||||||
if (this._headerBytes == HeaderLength)
|
if (this._headerBytes === HeaderLength)
|
||||||
{
|
{
|
||||||
if (this._packageLength <= 0 || this._packageLength > this._maxPackageSize)
|
if (this._packageLength <= 0 || this._packageLength > this._maxPackageSize)
|
||||||
throw new Error(["Package size is out of bounds: ", this._packageLength, "(max: ", this._maxPackageSize, "."].join(''));
|
throw new Error(["Package size is out of bounds: ", this._packageLength, "(max: ", this._maxPackageSize, "."].join(''));
|
||||||
@ -44,9 +44,9 @@ LengthPrefixMessageFramer.prototype._parse = function(bytes) {
|
|||||||
this._bufferIndex += copyCnt;
|
this._bufferIndex += copyCnt;
|
||||||
i += copyCnt - 1;
|
i += copyCnt - 1;
|
||||||
|
|
||||||
if (this._bufferIndex == this._packageLength)
|
if (this._bufferIndex === this._packageLength)
|
||||||
{
|
{
|
||||||
if (this._receivedHandler != null)
|
if (this._receivedHandler !== null)
|
||||||
this._receivedHandler(createBufferSegment(this._messageBuffer, 0, this._bufferIndex));
|
this._receivedHandler(createBufferSegment(this._messageBuffer, 0, this._bufferIndex));
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ function TcpConnection(log, connectionId, remoteEndPoint, onConnectionClosed) {
|
|||||||
TcpConnection.prototype._initSocket = function(socket) {
|
TcpConnection.prototype._initSocket = function(socket) {
|
||||||
this._socket = socket;
|
this._socket = socket;
|
||||||
this._localEndPoint = {host: socket.localAddress, port: socket.localPort};
|
this._localEndPoint = {host: socket.localAddress, port: socket.localPort};
|
||||||
|
this._remoteEndPoint.host = socket.remoteAddress;
|
||||||
|
|
||||||
this._socket.on('error', this._processError.bind(this));
|
this._socket.on('error', this._processError.bind(this));
|
||||||
this._socket.on('data', this._processReceive.bind(this));
|
this._socket.on('data', this._processReceive.bind(this));
|
||||||
@ -120,13 +121,13 @@ TcpConnection.prototype._closeInternal = function(err, reason) {
|
|||||||
if (this._closed) return;
|
if (this._closed) return;
|
||||||
this._closed = true;
|
this._closed = true;
|
||||||
|
|
||||||
if (this._socket != null) {
|
if (this._socket !== null) {
|
||||||
this._socket.end();
|
this._socket.end();
|
||||||
this._socket.unref();
|
this._socket.unref();
|
||||||
this._socket = null;
|
this._socket = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._onConnectionClosed != null)
|
if (this._onConnectionClosed !== null)
|
||||||
this._onConnectionClosed(this, err);
|
this._onConnectionClosed(this, err);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -135,7 +136,7 @@ TcpConnection.createConnectingConnection = function(
|
|||||||
onConnectionEstablished, onConnectionFailed, onConnectionClosed
|
onConnectionEstablished, onConnectionFailed, onConnectionClosed
|
||||||
) {
|
) {
|
||||||
var connection = new TcpConnection(log, connectionId, remoteEndPoint, onConnectionClosed);
|
var connection = new TcpConnection(log, connectionId, remoteEndPoint, onConnectionClosed);
|
||||||
var socket = net.connect(remoteEndPoint);
|
var socket = net.connect(remoteEndPoint.port, remoteEndPoint.host);
|
||||||
function onError(err) {
|
function onError(err) {
|
||||||
if (onConnectionFailed)
|
if (onConnectionFailed)
|
||||||
onConnectionFailed(connection, err);
|
onConnectionFailed(connection, err);
|
||||||
|
@ -7,6 +7,7 @@ var TcpPackage = require('../../systemData/tcpPackage');
|
|||||||
var TcpCommand = require('../../systemData/tcpCommand');
|
var TcpCommand = require('../../systemData/tcpCommand');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @private
|
||||||
* @param log
|
* @param log
|
||||||
* @param remoteEndPoint
|
* @param remoteEndPoint
|
||||||
* @param connectionId
|
* @param connectionId
|
||||||
@ -120,26 +121,26 @@ TcpPackageConnection.prototype._incomingMessageArrived = function(data) {
|
|||||||
var message = util.format("TcpPackageConnection: [%j, L%j, %s] ERROR for %s. Connection will be closed.",
|
var message = util.format("TcpPackageConnection: [%j, L%j, %s] ERROR for %s. Connection will be closed.",
|
||||||
this.remoteEndPoint, this.localEndPoint, this._connectionId,
|
this.remoteEndPoint, this.localEndPoint, this._connectionId,
|
||||||
valid ? TcpCommand.getName(pkg.command) : "<invalid package>");
|
valid ? TcpCommand.getName(pkg.command) : "<invalid package>");
|
||||||
if (this._onError != null)
|
if (this._onError !== null)
|
||||||
this._onError(this, e);
|
this._onError(this, e);
|
||||||
this._log.debug(e, message);
|
this._log.debug(e, message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TcpPackageConnection.prototype.startReceiving = function() {
|
TcpPackageConnection.prototype.startReceiving = function() {
|
||||||
if (this._connection == null)
|
if (this._connection === null)
|
||||||
throw new Error("Failed connection.");
|
throw new Error("Failed connection.");
|
||||||
this._connection.receive(this._onRawDataReceived.bind(this));
|
this._connection.receive(this._onRawDataReceived.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
TcpPackageConnection.prototype.enqueueSend = function(pkg) {
|
TcpPackageConnection.prototype.enqueueSend = function(pkg) {
|
||||||
if (this._connection == null)
|
if (this._connection === null)
|
||||||
throw new Error("Failed connection.");
|
throw new Error("Failed connection.");
|
||||||
this._connection.enqueueSend(this._framer.frameData(pkg.asBufferSegment()));
|
this._connection.enqueueSend(this._framer.frameData(pkg.asBufferSegment()));
|
||||||
};
|
};
|
||||||
|
|
||||||
TcpPackageConnection.prototype.close = function(reason) {
|
TcpPackageConnection.prototype.close = function(reason) {
|
||||||
if (this._connection == null)
|
if (this._connection === null)
|
||||||
throw new Error("Failed connection.");
|
throw new Error("Failed connection.");
|
||||||
this._connection.close(reason);
|
this._connection.close(reason);
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,7 @@ var util = require('util');
|
|||||||
var EventStoreSubsription = require('./eventStoreSubscription');
|
var EventStoreSubsription = require('./eventStoreSubscription');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @private
|
||||||
* @param {SubscriptionOperation} subscriptionOperation
|
* @param {SubscriptionOperation} subscriptionOperation
|
||||||
* @param {string} streamId
|
* @param {string} streamId
|
||||||
* @param {Position} lastCommitPosition
|
* @param {Position} lastCommitPosition
|
||||||
@ -10,15 +11,15 @@ var EventStoreSubsription = require('./eventStoreSubscription');
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @augments {EventStoreSubscription}
|
* @augments {EventStoreSubscription}
|
||||||
*/
|
*/
|
||||||
function VolatileEventStoreConnection(subscriptionOperation, streamId, lastCommitPosition, lastEventNumber) {
|
function VolatileEventStoreSubscription(subscriptionOperation, streamId, lastCommitPosition, lastEventNumber) {
|
||||||
EventStoreSubsription.call(this, streamId, lastCommitPosition, lastEventNumber);
|
EventStoreSubsription.call(this, streamId, lastCommitPosition, lastEventNumber);
|
||||||
|
|
||||||
this._subscriptionOperation = subscriptionOperation;
|
this._subscriptionOperation = subscriptionOperation;
|
||||||
}
|
}
|
||||||
util.inherits(VolatileEventStoreConnection, EventStoreSubsription);
|
util.inherits(VolatileEventStoreSubscription, EventStoreSubsription);
|
||||||
|
|
||||||
VolatileEventStoreConnection.prototype.unsubscribe = function() {
|
VolatileEventStoreSubscription.prototype.unsubscribe = function() {
|
||||||
this._subscriptionOperation.unsubscribe();
|
this._subscriptionOperation.unsubscribe();
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = VolatileEventStoreConnection;
|
module.exports = VolatileEventStoreSubscription;
|
@ -3,6 +3,7 @@ var client = require('../src/client');
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
'Append One Event To Stream Happy Path': function(test) {
|
'Append One Event To Stream Happy Path': function(test) {
|
||||||
|
test.expect(2);
|
||||||
var event = client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent');
|
var event = client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent');
|
||||||
this.conn.appendToStream(this.testStreamName, client.expectedVersion.any, event)
|
this.conn.appendToStream(this.testStreamName, client.expectedVersion.any, event)
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
@ -15,6 +16,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Append Multiple Events To Stream Happy Path': function(test) {
|
'Append Multiple Events To Stream Happy Path': function(test) {
|
||||||
|
test.expect(2);
|
||||||
const expectedVersion = 25;
|
const expectedVersion = 25;
|
||||||
var events = [];
|
var events = [];
|
||||||
for(var i = 0; i <= expectedVersion; i++) {
|
for(var i = 0; i <= expectedVersion; i++) {
|
||||||
@ -34,6 +36,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Append To Stream Wrong Expected Version': function(test) {
|
'Append To Stream Wrong Expected Version': function(test) {
|
||||||
|
test.expect(1);
|
||||||
var event = client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent');
|
var event = client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent');
|
||||||
this.conn.appendToStream(this.testStreamName, 10, event)
|
this.conn.appendToStream(this.testStreamName, 10, event)
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
@ -48,6 +51,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Append To Stream Deleted': function(test) {
|
'Append To Stream Deleted': function(test) {
|
||||||
|
test.expect(1);
|
||||||
var self = this;
|
var self = this;
|
||||||
this.conn.deleteStream(this.testStreamName, client.expectedVersion.noStream, true)
|
this.conn.deleteStream(this.testStreamName, client.expectedVersion.noStream, true)
|
||||||
.then(function() {
|
.then(function() {
|
||||||
@ -66,6 +70,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Append To Stream Access Denied': function(test) {
|
'Append To Stream Access Denied': function(test) {
|
||||||
|
test.expect(1);
|
||||||
var self = this;
|
var self = this;
|
||||||
var metadata = {$acl: {$w: "$admins"}};
|
var metadata = {$acl: {$w: "$admins"}};
|
||||||
this.conn.setStreamMetadataRaw(this.testStreamName, client.expectedVersion.noStream, metadata)
|
this.conn.setStreamMetadataRaw(this.testStreamName, client.expectedVersion.noStream, metadata)
|
||||||
|
@ -1,326 +0,0 @@
|
|||||||
var util = require('util');
|
|
||||||
var uuid = require('uuid');
|
|
||||||
var client = require('../src/client');
|
|
||||||
var NoopLogger = require('../src/common/log/noopLogger');
|
|
||||||
|
|
||||||
var consoleLogger = {
|
|
||||||
debug: function() {
|
|
||||||
var msg = util.format.apply(util, Array.prototype.slice.call(arguments));
|
|
||||||
util.log(msg);
|
|
||||||
},
|
|
||||||
info: function() {},
|
|
||||||
error: function() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
function createRandomEvent() {
|
|
||||||
return client.createJsonEventData(uuid.v4(), {a: uuid.v4(), b: Math.random()}, {createdAt: Date.now()}, 'testEvent');
|
|
||||||
}
|
|
||||||
|
|
||||||
var testStreamName = 'test-' + uuid.v4();
|
|
||||||
var userCredentialsForAll = new client.UserCredentials("admin", "changeit");
|
|
||||||
|
|
||||||
function testEvent(test, event, expectedVersion) {
|
|
||||||
if (!event) return;
|
|
||||||
test.ok(event.event, "Event has no 'event'.");
|
|
||||||
if (!event.event) return;
|
|
||||||
test.ok(event.event.eventNumber === expectedVersion, util.format("Wrong expected version. Expected: %d Got: %d", event.event.eventNumber, expectedVersion));
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
setUp: function(cb) {
|
|
||||||
var tcpEndPoint = {host: 'localhost', port: 1113};
|
|
||||||
var settings = {verboseLogging: false, log: new NoopLogger()};
|
|
||||||
//var settings = {verboseLogging: true, log: consoleLogger};
|
|
||||||
this.conn = client.EventStoreConnection.create(settings, tcpEndPoint);
|
|
||||||
this.connError = null;
|
|
||||||
var self = this;
|
|
||||||
this.conn.connect()
|
|
||||||
.catch(function(e) {
|
|
||||||
self.connError = e;
|
|
||||||
cb(e);
|
|
||||||
});
|
|
||||||
this.conn.on('connected', function() {
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
tearDown: function(cb) {
|
|
||||||
this.conn.close();
|
|
||||||
this.conn.on('closed', function() {
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
this.conn = null;
|
|
||||||
},
|
|
||||||
/*
|
|
||||||
'Test Connection': function(test) {
|
|
||||||
test.ok(this.connError === null, "Connection error: " + this.connError);
|
|
||||||
test.done();
|
|
||||||
},
|
|
||||||
'Test Append To Stream': function(test) {
|
|
||||||
var events = [
|
|
||||||
createRandomEvent()
|
|
||||||
];
|
|
||||||
this.conn.appendToStream(testStreamName, client.expectedVersion.any, events)
|
|
||||||
.then(function(result) {
|
|
||||||
test.ok(result, "No result.");
|
|
||||||
test.done();
|
|
||||||
})
|
|
||||||
.catch(function (err) {
|
|
||||||
test.done(err);
|
|
||||||
});
|
|
||||||
},*/
|
|
||||||
'Test Commit Two Events Using Transaction': function(test) {
|
|
||||||
this.conn.startTransaction(testStreamName, client.expectedVersion.any)
|
|
||||||
.then(function(trx) {
|
|
||||||
test.ok(trx, "No transaction.");
|
|
||||||
return Promise.all([trx, trx.write([createRandomEvent()])]);
|
|
||||||
})
|
|
||||||
.then(function(args) {
|
|
||||||
var trx = args[0];
|
|
||||||
return Promise.all([trx, trx.write([createRandomEvent()])]);
|
|
||||||
})
|
|
||||||
.then(function(args) {
|
|
||||||
var trx = args[0];
|
|
||||||
return trx.commit();
|
|
||||||
})
|
|
||||||
.then(function(result) {
|
|
||||||
test.ok(result, "No result.");
|
|
||||||
test.done();
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
test.done(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/*
|
|
||||||
'Test Read One Event': function(test) {
|
|
||||||
this.conn.readEvent(testStreamName, 0)
|
|
||||||
.then(function(result) {
|
|
||||||
test.ok(result, "No result.");
|
|
||||||
if (result)
|
|
||||||
test.ok(result.event, "No event. " + result.status);
|
|
||||||
test.done();
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
test.done(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
'Test Read Stream Forward': function(test) {
|
|
||||||
this.conn.readStreamEventsForward(testStreamName, 0, 100)
|
|
||||||
.then(function(result) {
|
|
||||||
test.ok(result, "No result.");
|
|
||||||
if (result)
|
|
||||||
test.ok(result.events.length === 3, "Expecting 3 events, got " + result.events.length);
|
|
||||||
for(var i = 0; i < 3; i++)
|
|
||||||
testEvent(test, result.events[i], i);
|
|
||||||
test.done();
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
test.done(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
'Test Read Stream Backward': function(test) {
|
|
||||||
this.conn.readStreamEventsBackward(testStreamName, 2, 100)
|
|
||||||
.then(function(result) {
|
|
||||||
test.ok(result, "No result.");
|
|
||||||
if (result)
|
|
||||||
test.ok(result.events.length === 3, "Expecting 3 events, got " + result.events.length);
|
|
||||||
for(var i = 0; i < 3; i++)
|
|
||||||
testEvent(test, result.events[i], 2-i);
|
|
||||||
test.done();
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
test.done(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
'Test Read All Forward': function(test) {
|
|
||||||
this.conn.readAllEventsForward(client.positions.start, 100, false, userCredentialsForAll)
|
|
||||||
.then(function(result) {
|
|
||||||
test.ok(result, "No result.");
|
|
||||||
if (result)
|
|
||||||
test.ok(result.events.length >= 3, "Expecting at least 3 events, got " + result.events.length);
|
|
||||||
for(var i = 1; i < result.events.length; i++)
|
|
||||||
test.ok(result.events[i].originalPosition.compareTo(result.events[i-1].originalPosition) > 0,
|
|
||||||
util.format("event[%d] position is not > event[%d] position.",
|
|
||||||
result.events[i].originalPosition,
|
|
||||||
result.events[i-1].originalPosition));
|
|
||||||
test.done();
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
test.done(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
'Test Read All Backward': function(test) {
|
|
||||||
this.conn.readAllEventsBackward(client.positions.end, 100, false, userCredentialsForAll)
|
|
||||||
.then(function(result) {
|
|
||||||
test.ok(result, "No result.");
|
|
||||||
if (result)
|
|
||||||
test.ok(result.events.length >= 3, "Expecting at least 3 events, got " + result.events.length);
|
|
||||||
for(var i = 1; i < result.events.length; i++)
|
|
||||||
test.ok(result.events[i].originalPosition.compareTo(result.events[i-1].originalPosition) < 0,
|
|
||||||
util.format("event[%d] position is not < event[%d] position.",
|
|
||||||
result.events[i].originalPosition,
|
|
||||||
result.events[i-1].originalPosition));
|
|
||||||
test.done();
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
test.done(err);
|
|
||||||
});
|
|
||||||
},*/
|
|
||||||
'Test Subscribe to Stream': function(test) {
|
|
||||||
var done = false;
|
|
||||||
function eventAppeared() {
|
|
||||||
if (!done) {
|
|
||||||
done = true;
|
|
||||||
test.done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function subscriptionDropped() {
|
|
||||||
if (!done) {
|
|
||||||
done = true;
|
|
||||||
test.done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var conn = this.conn;
|
|
||||||
this.conn.subscribeToStream(testStreamName, false, eventAppeared, subscriptionDropped)
|
|
||||||
.then(function(subscription) {
|
|
||||||
var events = [createRandomEvent()];
|
|
||||||
return conn.appendToStream(testStreamName, client.expectedVersion.any, events);
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
done = true;
|
|
||||||
test.done(err);
|
|
||||||
})
|
|
||||||
},
|
|
||||||
'Test Subscribe to All': function(test) {
|
|
||||||
var done = false;
|
|
||||||
function eventAppeared() {
|
|
||||||
if (!done) {
|
|
||||||
done = true;
|
|
||||||
test.done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function subscriptionDropped() {
|
|
||||||
if (!done) {
|
|
||||||
done = true;
|
|
||||||
test.done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var conn = this.conn;
|
|
||||||
this.conn.subscribeToAll(false, eventAppeared, subscriptionDropped, userCredentialsForAll)
|
|
||||||
.then(function(subscription) {
|
|
||||||
var events = [createRandomEvent()];
|
|
||||||
return conn.appendToStream(testStreamName, client.expectedVersion.any, events);
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
done = true;
|
|
||||||
test.done(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
'Test Subscribe to Stream From': function(test) {
|
|
||||||
var self = this;
|
|
||||||
var liveProcessing = false;
|
|
||||||
var catchUpEvents = [];
|
|
||||||
var liveEvents = [];
|
|
||||||
function eventAppeared(s, e) {
|
|
||||||
if (liveProcessing) {
|
|
||||||
liveEvents.push(e);
|
|
||||||
s.stop();
|
|
||||||
} else {
|
|
||||||
catchUpEvents.push(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function liveProcessingStarted() {
|
|
||||||
liveProcessing = true;
|
|
||||||
var events = [createRandomEvent()];
|
|
||||||
self.conn.appendToStream('test', client.expectedVersion.any, events);
|
|
||||||
}
|
|
||||||
function subscriptionDropped(connection, reason, error) {
|
|
||||||
test.ok(liveEvents.length === 1, "Expecting 1 live event, got " + liveEvents.length);
|
|
||||||
test.ok(catchUpEvents.length > 1, "Expecting at least 1 catchUp event, got " + catchUpEvents.length);
|
|
||||||
test.done(error);
|
|
||||||
}
|
|
||||||
var subscription = this.conn.subscribeToStreamFrom('test', null, false, eventAppeared, liveProcessingStarted, subscriptionDropped);
|
|
||||||
},
|
|
||||||
'Test Subscribe to All From': function(test) {
|
|
||||||
var self = this;
|
|
||||||
var liveProcessing = false;
|
|
||||||
var catchUpEvents = [];
|
|
||||||
var liveEvents = [];
|
|
||||||
function eventAppeared(s, e) {
|
|
||||||
if (liveProcessing) {
|
|
||||||
liveEvents.push(e);
|
|
||||||
s.stop();
|
|
||||||
} else {
|
|
||||||
catchUpEvents.push(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function liveProcessingStarted() {
|
|
||||||
liveProcessing = true;
|
|
||||||
var events = [createRandomEvent()];
|
|
||||||
self.conn.appendToStream(testStreamName, client.expectedVersion.any, events);
|
|
||||||
}
|
|
||||||
function subscriptionDropped(connection, reason, error) {
|
|
||||||
test.ok(liveEvents.length === 1, "Expecting 1 live event, got " + liveEvents.length);
|
|
||||||
test.ok(catchUpEvents.length > 1, "Expecting at least 1 catchUp event, got " + catchUpEvents.length);
|
|
||||||
test.done(error);
|
|
||||||
}
|
|
||||||
var subscription = this.conn.subscribeToAllFrom(null, false, eventAppeared, liveProcessingStarted, subscriptionDropped, userCredentialsForAll);
|
|
||||||
},
|
|
||||||
'Test Set Stream Metadata Raw': function(test) {
|
|
||||||
this.conn.setStreamMetadataRaw(testStreamName, client.expectedVersion.emptyStream, {$maxCount: 100})
|
|
||||||
.then(function(result) {
|
|
||||||
test.done();
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
test.done(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
'Test Get Stream Metadata Raw': function(test) {
|
|
||||||
this.conn.getStreamMetadataRaw(testStreamName)
|
|
||||||
.then(function(result) {
|
|
||||||
test.done();
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
test.done(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
'Test Create Persistent Subscription': function(test) {
|
|
||||||
var settings = client.PersistentSubscriptionSettings.create();
|
|
||||||
this.conn.createPersistentSubscription(testStreamName, 'consumer-1', settings, userCredentialsForAll)
|
|
||||||
.then(function(result) {
|
|
||||||
test.done();
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
test.done(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
//TODO: Update Persistent Subscription
|
|
||||||
'Test ConnectTo Persistent Subscription': function(test) {
|
|
||||||
function eventAppeared(s, e) {
|
|
||||||
s.stop();
|
|
||||||
}
|
|
||||||
function subscriptionDropped(connection, reason, error) {
|
|
||||||
test.done(error);
|
|
||||||
}
|
|
||||||
var subscription = this.conn.connectToPersistentSubscription(testStreamName, 'consumer-1', eventAppeared, subscriptionDropped);
|
|
||||||
this.conn.appendToStream(testStreamName, client.expectedVersion.any, [createRandomEvent()]);
|
|
||||||
},
|
|
||||||
'Test Delete Persistent Subscription': function(test) {
|
|
||||||
this.conn.deletePersistentSubscription(testStreamName, 'consumer-1', userCredentialsForAll)
|
|
||||||
.then(function(result) {
|
|
||||||
test.done();
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
test.done(err);
|
|
||||||
});
|
|
||||||
}/*,
|
|
||||||
'Test Delete Stream': function(test) {
|
|
||||||
this.conn.deleteStream(testStreamName, client.expectedVersion.any)
|
|
||||||
.then(function(result) {
|
|
||||||
test.ok(result, "No result.");
|
|
||||||
test.done();
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
test.done(err);
|
|
||||||
});
|
|
||||||
}*/
|
|
||||||
};
|
|
32
test/cluster/dns_test.js
Normal file
32
test/cluster/dns_test.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
var client = require('../../src/client');
|
||||||
|
var uuid = require('uuid');
|
||||||
|
|
||||||
|
var settings = {
|
||||||
|
log: {
|
||||||
|
info: console.log,
|
||||||
|
error: console.log,
|
||||||
|
debug: console.log
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var conn = client.createConnection(settings, "discover://es.nicdex.com:2113");
|
||||||
|
console.log('Connecting...');
|
||||||
|
conn.on('connected', function (tcpEndPoint) {
|
||||||
|
console.log('Connected to', tcpEndPoint);
|
||||||
|
setTimeout(function () {
|
||||||
|
conn.appendToStream('test-' + uuid.v4(), client.expectedVersion.noStream, [
|
||||||
|
client.createJsonEventData(uuid.v4(), {abc: 123}, {}, 'myEvent')
|
||||||
|
]);
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
conn.on('error', function (err) {
|
||||||
|
console.log(err.stack);
|
||||||
|
});
|
||||||
|
conn.on('closed', function (reason) {
|
||||||
|
console.log('Connection closed:', reason);
|
||||||
|
process.exit(-1);
|
||||||
|
});
|
||||||
|
conn.connect()
|
||||||
|
.catch(function (err) {
|
||||||
|
console.log(err.stack);
|
||||||
|
process.exit(-1);
|
||||||
|
});
|
37
test/cluster/gossipSeeds_test.js
Normal file
37
test/cluster/gossipSeeds_test.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
var client = require('../../src/client');
|
||||||
|
var uuid = require('uuid');
|
||||||
|
|
||||||
|
var settings = {
|
||||||
|
log: {
|
||||||
|
info: console.log,
|
||||||
|
error: console.log,
|
||||||
|
debug: console.log
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var gossipSeeds = [
|
||||||
|
new client.GossipSeed({host: '192.168.33.10', port: 2113}),
|
||||||
|
new client.GossipSeed({host: '192.168.33.11', port: 2113}),
|
||||||
|
new client.GossipSeed({host: '192.168.33.12', port: 2113})
|
||||||
|
];
|
||||||
|
var conn = client.createConnection(settings, gossipSeeds);
|
||||||
|
console.log('Connecting...');
|
||||||
|
conn.on('connected', function (tcpEndPoint) {
|
||||||
|
console.log('Connected to', tcpEndPoint);
|
||||||
|
setTimeout(function () {
|
||||||
|
conn.appendToStream('test-' + uuid.v4(), client.expectedVersion.noStream, [
|
||||||
|
client.createJsonEventData(uuid.v4(), {abc: 123}, {}, 'myEvent')
|
||||||
|
]);
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
conn.on('error', function (err) {
|
||||||
|
console.log(err.stack);
|
||||||
|
});
|
||||||
|
conn.on('closed', function (reason) {
|
||||||
|
console.log('Connection closed:', reason);
|
||||||
|
process.exit(-1);
|
||||||
|
});
|
||||||
|
conn.connect()
|
||||||
|
.catch(function (err) {
|
||||||
|
console.log(err.stack);
|
||||||
|
process.exit(-1);
|
||||||
|
});
|
@ -34,7 +34,8 @@ function setUp(cb) {
|
|||||||
settings.log.error(error, "Connection to %j failed.", tcpEndPoint);
|
settings.log.error(error, "Connection to %j failed.", tcpEndPoint);
|
||||||
cb(error);
|
cb(error);
|
||||||
});
|
});
|
||||||
this.conn.on('connected', function () {
|
this.conn.on('connected', function (tcpEndPoint) {
|
||||||
|
if (connected) return;
|
||||||
settings.log.debug("Connected to %j.", tcpEndPoint);
|
settings.log.debug("Connected to %j.", tcpEndPoint);
|
||||||
connected = true;
|
connected = true;
|
||||||
cb();
|
cb();
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
var client = require('../src/client.js');
|
var client = require('../src/client.js');
|
||||||
|
var GossipSeed = require('../src/gossipSeed');
|
||||||
|
|
||||||
var testBase = require('./common/base_test');
|
var testBase = require('./common/base_test');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
'Connect To Endpoint Happy Path': function(test) {
|
'Connect To Endpoint Happy Path': function(test) {
|
||||||
var tcpEndpoint = {hostname: 'localhost', port: 1113};
|
test.expect(1);
|
||||||
|
var tcpEndpoint = {host: 'localhost', port: 1113};
|
||||||
var conn = client.EventStoreConnection.create(testBase.settings(), tcpEndpoint);
|
var conn = client.EventStoreConnection.create(testBase.settings(), tcpEndpoint);
|
||||||
conn.connect()
|
conn.connect()
|
||||||
.catch(function(err) {
|
.catch(function(err) {
|
||||||
@ -22,15 +24,16 @@ module.exports = {
|
|||||||
test.done();
|
test.done();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'Connect To Endpoint That Don\'t Exist': function(test) {
|
'Connect To Endpoint That Doesn\'t Exist': function(test) {
|
||||||
var tcpEndpoint = {hostname: 'localhost', port: 1114};
|
test.expect(1);
|
||||||
|
var tcpEndpoint = {host: 'localhost', port: 11112};
|
||||||
var conn = client.EventStoreConnection.create(testBase.settings({maxReconnections:1}), tcpEndpoint);
|
var conn = client.EventStoreConnection.create(testBase.settings({maxReconnections:1}), tcpEndpoint);
|
||||||
conn.connect()
|
conn.connect()
|
||||||
.catch(function (err) {
|
.catch(function (err) {
|
||||||
test.done(err);
|
test.done(err);
|
||||||
});
|
});
|
||||||
conn.on('connected', function () {
|
conn.on('connected', function () {
|
||||||
test.fail("Should not be able to connect.");
|
test.ok(false, "Should not be able to connect.");
|
||||||
test.done();
|
test.done();
|
||||||
});
|
});
|
||||||
conn.on('error', function (err) {
|
conn.on('error', function (err) {
|
||||||
@ -40,7 +43,36 @@ module.exports = {
|
|||||||
test.ok(reason.indexOf("Reconnection limit reached") === 0, "Wrong expected reason.");
|
test.ok(reason.indexOf("Reconnection limit reached") === 0, "Wrong expected reason.");
|
||||||
test.done();
|
test.done();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
'Create a connection with tcp://host:port string': function(test) {
|
||||||
|
var conn = client.createConnection({}, 'tcp://localhost:1113');
|
||||||
|
conn.close();
|
||||||
|
test.done();
|
||||||
|
}/*,
|
||||||
|
'Connect to Cluster using gossip seeds': function (test) {
|
||||||
|
test.expect(1);
|
||||||
|
var gossipSeeds = [
|
||||||
|
new GossipSeed({host: 'localhost', port: 1113}),
|
||||||
|
new GossipSeed({host: 'localhost', port: 2113}),
|
||||||
|
new GossipSeed({host: 'localhost', port: 3113})
|
||||||
|
];
|
||||||
|
var conn = client.EventStoreConnection.create(testBase.settings(), gossipSeeds);
|
||||||
|
conn.connect()
|
||||||
|
.catch(function(err) {
|
||||||
|
test.done(err);
|
||||||
|
});
|
||||||
|
conn.on('connected', function(endPoint){
|
||||||
|
test.ok(endPoint, "no endpoint");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
conn.on('error', done);
|
||||||
|
|
||||||
|
function done(err) {
|
||||||
|
conn.close();
|
||||||
|
if (err) return test.done(err);
|
||||||
|
test.done();
|
||||||
|
}
|
||||||
|
}*/
|
||||||
};
|
};
|
||||||
|
|
||||||
testBase.init(module.exports, false);
|
testBase.init(module.exports, false);
|
@ -14,6 +14,7 @@ module.exports = {
|
|||||||
.catch(cb);
|
.catch(cb);
|
||||||
},
|
},
|
||||||
'Test Delete Stream Soft Happy Path': function(test) {
|
'Test Delete Stream Soft Happy Path': function(test) {
|
||||||
|
test.expect(4);
|
||||||
var self = this;
|
var self = this;
|
||||||
this.conn.deleteStream(this.testStreamName, 1, false)
|
this.conn.deleteStream(this.testStreamName, 1, false)
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
@ -31,6 +32,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Test Delete Stream Hard Happy Path': function(test) {
|
'Test Delete Stream Hard Happy Path': function(test) {
|
||||||
|
test.expect(4);
|
||||||
var self = this;
|
var self = this;
|
||||||
this.conn.deleteStream(this.testStreamName, 1, true)
|
this.conn.deleteStream(this.testStreamName, 1, true)
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
@ -48,6 +50,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Test Delete Stream With Wrong Expected Version': function(test) {
|
'Test Delete Stream With Wrong Expected Version': function(test) {
|
||||||
|
test.expect(1);
|
||||||
this.conn.deleteStream(this.testStreamName, 10)
|
this.conn.deleteStream(this.testStreamName, 10)
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
test.fail("Delete succeeded but should have failed.");
|
test.fail("Delete succeeded but should have failed.");
|
||||||
@ -61,6 +64,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Test Delete Stream With No Access': function(test) {
|
'Test Delete Stream With No Access': function(test) {
|
||||||
|
test.expect(1);
|
||||||
var self = this;
|
var self = this;
|
||||||
this.conn.setStreamMetadataRaw(this.testStreamName, client.expectedVersion.any, {$acl: {$d: "$admins"}})
|
this.conn.setStreamMetadataRaw(this.testStreamName, client.expectedVersion.any, {$acl: {$d: "$admins"}})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
@ -78,6 +82,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Test Delete Stream Hard When Already Deleted': function(test) {
|
'Test Delete Stream Hard When Already Deleted': function(test) {
|
||||||
|
test.expect(1);
|
||||||
var self = this;
|
var self = this;
|
||||||
this.conn.deleteStream(this.testStreamName, 1, true)
|
this.conn.deleteStream(this.testStreamName, 1, true)
|
||||||
.then(function() {
|
.then(function() {
|
||||||
|
26
test/metadata_test.js
Normal file
26
test/metadata_test.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
var util = require('util');
|
||||||
|
var uuid = require('uuid');
|
||||||
|
var client = require('../src/client');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'Test Set Stream Metadata Raw': function(test) {
|
||||||
|
this.conn.setStreamMetadataRaw(this.testStreamName, client.expectedVersion.emptyStream, {$maxCount: 100})
|
||||||
|
.then(function(result) {
|
||||||
|
test.done();
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
test.done(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'Test Get Stream Metadata Raw': function(test) {
|
||||||
|
this.conn.getStreamMetadataRaw(this.testStreamName)
|
||||||
|
.then(function(result) {
|
||||||
|
test.done();
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
test.done(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
require('./common/base_test').init(module.exports);
|
57
test/persistentSubscription_test.js
Normal file
57
test/persistentSubscription_test.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
var util = require('util');
|
||||||
|
var uuid = require('uuid');
|
||||||
|
var client = require('../src/client');
|
||||||
|
const adminCredentials = new client.UserCredentials("admin", "changeit");
|
||||||
|
|
||||||
|
function createRandomEvent() {
|
||||||
|
return client.createJsonEventData(uuid.v4(), {a: uuid.v4(), b: Math.random()}, {createdAt: Date.now()}, 'testEvent');
|
||||||
|
}
|
||||||
|
|
||||||
|
var testStreamName = 'test-' + uuid.v4();
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'Test Create Persistent Subscription': function(test) {
|
||||||
|
var settings = client.PersistentSubscriptionSettings.create();
|
||||||
|
this.conn.createPersistentSubscription(testStreamName, 'consumer-1', settings, adminCredentials)
|
||||||
|
.then(function(result) {
|
||||||
|
test.done();
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
test.done(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
//TODO: Update Persistent Subscription
|
||||||
|
'Test ConnectTo Persistent Subscription': function(test) {
|
||||||
|
test.expect(2);
|
||||||
|
var _doneCount = 0;
|
||||||
|
function done(err) {
|
||||||
|
test.ok(!err, err ? err.stack : '');
|
||||||
|
_doneCount++;
|
||||||
|
if (_doneCount < 2) return;
|
||||||
|
test.done();
|
||||||
|
}
|
||||||
|
function eventAppeared(s, e) {
|
||||||
|
s.stop();
|
||||||
|
}
|
||||||
|
function subscriptionDropped(connection, reason, error) {
|
||||||
|
done(error);
|
||||||
|
}
|
||||||
|
var subscription = this.conn.connectToPersistentSubscription(testStreamName, 'consumer-1', eventAppeared, subscriptionDropped);
|
||||||
|
this.conn.appendToStream(testStreamName, client.expectedVersion.any, [createRandomEvent()])
|
||||||
|
.then(function () {
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
},
|
||||||
|
'Test Delete Persistent Subscription': function(test) {
|
||||||
|
this.conn.deletePersistentSubscription(testStreamName, 'consumer-1', adminCredentials)
|
||||||
|
.then(function(result) {
|
||||||
|
test.done();
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
test.done(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
require('./common/base_test').init(module.exports);
|
@ -37,6 +37,7 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
'Read All Events Backward Happy Path': function(test) {
|
'Read All Events Backward Happy Path': function(test) {
|
||||||
|
test.expect(4 + maxCount);
|
||||||
var self = this;
|
var self = this;
|
||||||
this.conn.readAllEventsBackward(client.positions.end, maxCount, false, allCredentials)
|
this.conn.readAllEventsBackward(client.positions.end, maxCount, false, allCredentials)
|
||||||
.then(function(slice) {
|
.then(function(slice) {
|
||||||
@ -56,6 +57,7 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
'Read All Events Backward With No Access': function(test) {
|
'Read All Events Backward With No Access': function(test) {
|
||||||
|
test.expect(1);
|
||||||
this.conn.readAllEventsBackward(client.positions.end, maxCount)
|
this.conn.readAllEventsBackward(client.positions.end, maxCount)
|
||||||
.then(function(slice) {
|
.then(function(slice) {
|
||||||
test.fail("readAllEventsBackward succeeded but should have failed.");
|
test.fail("readAllEventsBackward succeeded but should have failed.");
|
||||||
|
@ -37,6 +37,7 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
'Read All Events Forward Happy Path': function(test) {
|
'Read All Events Forward Happy Path': function(test) {
|
||||||
|
test.expect(5 + maxCount);
|
||||||
this.conn.readAllEventsForward(client.positions.start, maxCount, false, allCredentials)
|
this.conn.readAllEventsForward(client.positions.start, maxCount, false, allCredentials)
|
||||||
.then(function(slice) {
|
.then(function(slice) {
|
||||||
test.areEqual('slice.readDirection', slice.readDirection, 'forward');
|
test.areEqual('slice.readDirection', slice.readDirection, 'forward');
|
||||||
@ -57,6 +58,7 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
'Read All Events Forward With No Access': function(test) {
|
'Read All Events Forward With No Access': function(test) {
|
||||||
|
test.expect(1);
|
||||||
this.conn.readAllEventsForward(client.positions.start, maxCount)
|
this.conn.readAllEventsForward(client.positions.start, maxCount)
|
||||||
.then(function(slice) {
|
.then(function(slice) {
|
||||||
test.fail("readAllEventsForward succeeded but should have failed.");
|
test.fail("readAllEventsForward succeeded but should have failed.");
|
||||||
|
@ -16,6 +16,7 @@ module.exports = {
|
|||||||
.catch(cb);
|
.catch(cb);
|
||||||
},
|
},
|
||||||
'Read Event Happy Path': function(test) {
|
'Read Event Happy Path': function(test) {
|
||||||
|
test.expect(8);
|
||||||
var self = this;
|
var self = this;
|
||||||
this.conn.readEvent(this.testStreamName, 0)
|
this.conn.readEvent(this.testStreamName, 0)
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
@ -35,6 +36,7 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
'Read Event From Non-Existing Stream': function(test) {
|
'Read Event From Non-Existing Stream': function(test) {
|
||||||
|
test.expect(4);
|
||||||
var anotherStream = 'test' + uuid.v4();
|
var anotherStream = 'test' + uuid.v4();
|
||||||
this.conn.readEvent(anotherStream, 0)
|
this.conn.readEvent(anotherStream, 0)
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
@ -49,6 +51,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Read Event From Deleted Stream': function(test) {
|
'Read Event From Deleted Stream': function(test) {
|
||||||
|
test.expect(4);
|
||||||
var self = this;
|
var self = this;
|
||||||
this.conn.deleteStream(this.testStreamName, 0, true)
|
this.conn.deleteStream(this.testStreamName, 0, true)
|
||||||
.then(function() {
|
.then(function() {
|
||||||
@ -66,6 +69,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Read Event With Inexisting Version': function(test) {
|
'Read Event With Inexisting Version': function(test) {
|
||||||
|
test.expect(4);
|
||||||
var self = this;
|
var self = this;
|
||||||
return self.conn.readEvent(self.testStreamName, 1)
|
return self.conn.readEvent(self.testStreamName, 1)
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
@ -80,6 +84,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Read Event With No Access': function(test) {
|
'Read Event With No Access': function(test) {
|
||||||
|
test.expect(1);
|
||||||
var self = this;
|
var self = this;
|
||||||
var metadata = {$acl: {$r: '$admins'}};
|
var metadata = {$acl: {$r: '$admins'}};
|
||||||
this.conn.setStreamMetadataRaw(self.testStreamName, client.expectedVersion.noStream, metadata)
|
this.conn.setStreamMetadataRaw(self.testStreamName, client.expectedVersion.noStream, metadata)
|
||||||
|
@ -16,6 +16,7 @@ module.exports = {
|
|||||||
.catch(cb);
|
.catch(cb);
|
||||||
},
|
},
|
||||||
'Read Stream Events Backward Happy Path': function(test) {
|
'Read Stream Events Backward Happy Path': function(test) {
|
||||||
|
test.expect(7 + (streamSize * 6));
|
||||||
var self = this;
|
var self = this;
|
||||||
this.conn.readStreamEventsBackward(this.testStreamName, streamSize-1, streamSize)
|
this.conn.readStreamEventsBackward(this.testStreamName, streamSize-1, streamSize)
|
||||||
.then(function(slice) {
|
.then(function(slice) {
|
||||||
@ -38,6 +39,7 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
'Read Stream Events Backward With Non-Existing Stream': function(test) {
|
'Read Stream Events Backward With Non-Existing Stream': function(test) {
|
||||||
|
test.expect(4);
|
||||||
var anotherStream = 'test' + uuid.v4();
|
var anotherStream = 'test' + uuid.v4();
|
||||||
this.conn.readStreamEventsBackward(anotherStream, streamSize-1, streamSize)
|
this.conn.readStreamEventsBackward(anotherStream, streamSize-1, streamSize)
|
||||||
.then(function(slice) {
|
.then(function(slice) {
|
||||||
@ -52,6 +54,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Read Stream Events Backward With Deleted Stream': function(test) {
|
'Read Stream Events Backward With Deleted Stream': function(test) {
|
||||||
|
test.expect(4);
|
||||||
var self = this;
|
var self = this;
|
||||||
this.conn.deleteStream(this.testStreamName, streamSize-1, true)
|
this.conn.deleteStream(this.testStreamName, streamSize-1, true)
|
||||||
.then(function() {
|
.then(function() {
|
||||||
@ -69,6 +72,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Read Stream Events Backward With Inexisting Version': function(test) {
|
'Read Stream Events Backward With Inexisting Version': function(test) {
|
||||||
|
test.expect(4);
|
||||||
var self = this;
|
var self = this;
|
||||||
return self.conn.readStreamEventsBackward(self.testStreamName, streamSize * 2, streamSize)
|
return self.conn.readStreamEventsBackward(self.testStreamName, streamSize * 2, streamSize)
|
||||||
.then(function(slice) {
|
.then(function(slice) {
|
||||||
@ -83,6 +87,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Read Stream Events Backward With No Access': function(test) {
|
'Read Stream Events Backward With No Access': function(test) {
|
||||||
|
test.expect(1);
|
||||||
var self = this;
|
var self = this;
|
||||||
var metadata = {$acl: {$r: '$admins'}};
|
var metadata = {$acl: {$r: '$admins'}};
|
||||||
this.conn.setStreamMetadataRaw(self.testStreamName, client.expectedVersion.noStream, metadata)
|
this.conn.setStreamMetadataRaw(self.testStreamName, client.expectedVersion.noStream, metadata)
|
||||||
|
@ -16,6 +16,7 @@ module.exports = {
|
|||||||
.catch(cb);
|
.catch(cb);
|
||||||
},
|
},
|
||||||
'Read Stream Events Forward Happy Path': function(test) {
|
'Read Stream Events Forward Happy Path': function(test) {
|
||||||
|
test.expect(7 + (streamSize * 6));
|
||||||
var self = this;
|
var self = this;
|
||||||
this.conn.readStreamEventsForward(this.testStreamName, 0, streamSize)
|
this.conn.readStreamEventsForward(this.testStreamName, 0, streamSize)
|
||||||
.then(function(slice) {
|
.then(function(slice) {
|
||||||
@ -37,6 +38,7 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
'Read Stream Events Forward With Non-Existing Stream': function(test) {
|
'Read Stream Events Forward With Non-Existing Stream': function(test) {
|
||||||
|
test.expect(4);
|
||||||
var anotherStream = 'test' + uuid.v4();
|
var anotherStream = 'test' + uuid.v4();
|
||||||
this.conn.readStreamEventsForward(anotherStream, 0, streamSize)
|
this.conn.readStreamEventsForward(anotherStream, 0, streamSize)
|
||||||
.then(function(slice) {
|
.then(function(slice) {
|
||||||
@ -51,6 +53,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Read Stream Events Forward With Deleted Stream': function(test) {
|
'Read Stream Events Forward With Deleted Stream': function(test) {
|
||||||
|
test.expect(4);
|
||||||
var self = this;
|
var self = this;
|
||||||
this.conn.deleteStream(this.testStreamName, streamSize-1, true)
|
this.conn.deleteStream(this.testStreamName, streamSize-1, true)
|
||||||
.then(function() {
|
.then(function() {
|
||||||
@ -68,6 +71,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Read Stream Events Forward With Inexisting Version': function(test) {
|
'Read Stream Events Forward With Inexisting Version': function(test) {
|
||||||
|
test.expect(4);
|
||||||
var self = this;
|
var self = this;
|
||||||
return self.conn.readStreamEventsForward(self.testStreamName, streamSize * 2, streamSize)
|
return self.conn.readStreamEventsForward(self.testStreamName, streamSize * 2, streamSize)
|
||||||
.then(function(slice) {
|
.then(function(slice) {
|
||||||
@ -82,6 +86,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
'Read Stream Events Forward With No Access': function(test) {
|
'Read Stream Events Forward With No Access': function(test) {
|
||||||
|
test.expect(1);
|
||||||
var self = this;
|
var self = this;
|
||||||
var metadata = {$acl: {$r: '$admins'}};
|
var metadata = {$acl: {$r: '$admins'}};
|
||||||
this.conn.setStreamMetadataRaw(self.testStreamName, client.expectedVersion.noStream, metadata)
|
this.conn.setStreamMetadataRaw(self.testStreamName, client.expectedVersion.noStream, metadata)
|
||||||
|
132
test/subscribeToAllFrom_test.js
Normal file
132
test/subscribeToAllFrom_test.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
var util = require('util');
|
||||||
|
var uuid = require('uuid');
|
||||||
|
var client = require('../src/client');
|
||||||
|
const allCredentials = new client.UserCredentials("admin", "changeit");
|
||||||
|
|
||||||
|
function createRandomEvent() {
|
||||||
|
return client.createJsonEventData(uuid.v4(), {a: uuid.v4(), b: Math.random()}, {createdAt: Date.now()}, 'testEvent');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'Test Subscribe to All From (Start)': function(test) {
|
||||||
|
test.expect(4);
|
||||||
|
var self = this;
|
||||||
|
var liveProcessing = false;
|
||||||
|
var catchUpEvents = [];
|
||||||
|
var liveEvents = [];
|
||||||
|
var _doneCount = 0;
|
||||||
|
function done(err) {
|
||||||
|
test.ok(!err, err ? err.stack : '');
|
||||||
|
_doneCount++;
|
||||||
|
if (_doneCount < 2) return;
|
||||||
|
test.done();
|
||||||
|
}
|
||||||
|
function eventAppeared(s, e) {
|
||||||
|
if (liveProcessing) {
|
||||||
|
liveEvents.push(e);
|
||||||
|
s.stop();
|
||||||
|
} else {
|
||||||
|
catchUpEvents.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function liveProcessingStarted() {
|
||||||
|
liveProcessing = true;
|
||||||
|
var events = [createRandomEvent()];
|
||||||
|
self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, events)
|
||||||
|
.then(function () {
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
}
|
||||||
|
function subscriptionDropped(connection, reason, error) {
|
||||||
|
test.ok(liveEvents.length === 1, "Expecting 1 live event, got " + liveEvents.length);
|
||||||
|
test.ok(catchUpEvents.length > 1, "Expecting at least 1 catchUp event, got " + catchUpEvents.length);
|
||||||
|
done(error);
|
||||||
|
}
|
||||||
|
var subscription = this.conn.subscribeToAllFrom(null, false, eventAppeared, liveProcessingStarted, subscriptionDropped, allCredentials);
|
||||||
|
},
|
||||||
|
'Test Subscribe to All From (Position)': function(test) {
|
||||||
|
test.expect(5);
|
||||||
|
var self = this;
|
||||||
|
var liveProcessing = false;
|
||||||
|
var catchUpEvents = [];
|
||||||
|
var liveEvents = [];
|
||||||
|
var _doneCount = 0;
|
||||||
|
function done(err) {
|
||||||
|
test.ok(!err, err ? err.stack : '');
|
||||||
|
_doneCount++;
|
||||||
|
if (_doneCount < 2) return;
|
||||||
|
test.done();
|
||||||
|
}
|
||||||
|
function eventAppeared(s, e) {
|
||||||
|
if (liveProcessing) {
|
||||||
|
liveEvents.push(e);
|
||||||
|
s.stop();
|
||||||
|
} else {
|
||||||
|
catchUpEvents.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function liveProcessingStarted() {
|
||||||
|
liveProcessing = true;
|
||||||
|
var events = [createRandomEvent()];
|
||||||
|
self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, events)
|
||||||
|
.then(function () {
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
}
|
||||||
|
function subscriptionDropped(connection, reason, error) {
|
||||||
|
test.ok(liveEvents.length === 1, "Expecting 1 live event, got " + liveEvents.length);
|
||||||
|
test.ok(catchUpEvents.length > 1, "Expecting at least 1 catchUp event, got " + catchUpEvents.length);
|
||||||
|
done(error);
|
||||||
|
}
|
||||||
|
this.conn.readAllEventsForward(client.positions.start, 512, true, allCredentials)
|
||||||
|
.then(function (slice) {
|
||||||
|
var subscription = self.conn.subscribeToAllFrom(slice.nextPosition, false, eventAppeared, liveProcessingStarted, subscriptionDropped, allCredentials);
|
||||||
|
test.ok(subscription, "Subscription is null/undefined.");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'Test Subscribe to All From (End)': function(test) {
|
||||||
|
test.expect(5);
|
||||||
|
var self = this;
|
||||||
|
var liveProcessing = false;
|
||||||
|
var catchUpEvents = [];
|
||||||
|
var liveEvents = [];
|
||||||
|
var _doneCount = 0;
|
||||||
|
function done(err) {
|
||||||
|
test.ok(!err, err ? err.stack : '');
|
||||||
|
_doneCount++;
|
||||||
|
if (_doneCount < 2) return;
|
||||||
|
test.done();
|
||||||
|
}
|
||||||
|
function eventAppeared(s, e) {
|
||||||
|
if (liveProcessing) {
|
||||||
|
liveEvents.push(e);
|
||||||
|
s.stop();
|
||||||
|
} else {
|
||||||
|
catchUpEvents.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function liveProcessingStarted() {
|
||||||
|
liveProcessing = true;
|
||||||
|
var events = [createRandomEvent()];
|
||||||
|
self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, events)
|
||||||
|
.then(function () {
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
}
|
||||||
|
function subscriptionDropped(connection, reason, error) {
|
||||||
|
test.ok(liveEvents.length === 1, "Expecting 1 live event, got " + liveEvents.length);
|
||||||
|
test.ok(catchUpEvents.length === 0, "Expecting 0 catchUp event, got " + catchUpEvents.length);
|
||||||
|
done(error);
|
||||||
|
}
|
||||||
|
this.conn.readAllEventsForward(client.positions.end, 512, true, allCredentials)
|
||||||
|
.then(function (slice) {
|
||||||
|
var subscription = self.conn.subscribeToAllFrom(slice.nextPosition, false, eventAppeared, liveProcessingStarted, subscriptionDropped, allCredentials);
|
||||||
|
test.ok(subscription, "Subscription is null/undefined.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
require('./common/base_test').init(module.exports);
|
56
test/subscribeToAll_test.js
Normal file
56
test/subscribeToAll_test.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
const uuid = require('uuid');
|
||||||
|
const client = require('../src/client');
|
||||||
|
const allCredentials = new client.UserCredentials("admin", "changeit");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'Test Subscribe To All Happy Path': function(test) {
|
||||||
|
const resolveLinkTos = false;
|
||||||
|
const numberOfPublishedEvents = 5;
|
||||||
|
test.expect(numberOfPublishedEvents + 4);
|
||||||
|
|
||||||
|
var _doneCount = 0;
|
||||||
|
function done(err) {
|
||||||
|
test.ok(!err, err ? err.stack : '');
|
||||||
|
if (++_doneCount < 2) return;
|
||||||
|
test.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
var publishedEvents = [];
|
||||||
|
for(var i=0;i<numberOfPublishedEvents;i++)
|
||||||
|
publishedEvents.push(client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'anEvent'));
|
||||||
|
|
||||||
|
function testAllPublishedEventsAppeared() {
|
||||||
|
test.areEqual("receivedEvents.length", receivedEvents.length, numberOfPublishedEvents);
|
||||||
|
}
|
||||||
|
function testEventsAppearedInCorrectOrder() {
|
||||||
|
for (var j = 0; j < numberOfPublishedEvents; j++)
|
||||||
|
test.ok(receivedEvents[j].originalEvent.eventId === publishedEvents[j].eventId,
|
||||||
|
"receivedEvents[" + j + "] != publishedEvents[" + j + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
var receivedEvents = [];
|
||||||
|
function eventAppeared(subscription, event) {
|
||||||
|
receivedEvents.push(event);
|
||||||
|
if (receivedEvents.length === numberOfPublishedEvents) subscription.close();
|
||||||
|
}
|
||||||
|
function subscriptionDropped(subscription, reason, error) {
|
||||||
|
if (error) return done(error);
|
||||||
|
testAllPublishedEventsAppeared();
|
||||||
|
testEventsAppearedInCorrectOrder();
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
var self = this;
|
||||||
|
this.conn.subscribeToAll(resolveLinkTos, eventAppeared, subscriptionDropped, allCredentials)
|
||||||
|
.then(function(subscription) {
|
||||||
|
test.areEqual("subscription.isSubscribedToAll", subscription.isSubscribedToAll, true);
|
||||||
|
|
||||||
|
return self.conn.appendToStream(self.testStreamName, client.expectedVersion.emptyStream, publishedEvents);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(test.done)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
require('./common/base_test').init(module.exports);
|
61
test/subscribeToStreamFrom_test.js
Normal file
61
test/subscribeToStreamFrom_test.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
var util = require('util');
|
||||||
|
var uuid = require('uuid');
|
||||||
|
var client = require('../src/client');
|
||||||
|
|
||||||
|
function createRandomEvent() {
|
||||||
|
return client.createJsonEventData(uuid.v4(), {a: uuid.v4(), b: Math.random()}, {createdAt: Date.now()}, 'testEvent');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'Test Subscribe to Stream From Happy Path': function(test) {
|
||||||
|
test.expect(8);
|
||||||
|
var self = this;
|
||||||
|
var liveProcessing = false;
|
||||||
|
var catchUpEvents = [];
|
||||||
|
var liveEvents = [];
|
||||||
|
var _doneCount = 0;
|
||||||
|
|
||||||
|
function done(err) {
|
||||||
|
test.ok(!err, err ? err.stack : '');
|
||||||
|
if (++_doneCount < 2) return;
|
||||||
|
test.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventAppeared(s, e) {
|
||||||
|
if (liveProcessing) {
|
||||||
|
liveEvents.push(e);
|
||||||
|
s.stop();
|
||||||
|
} else {
|
||||||
|
catchUpEvents.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function liveProcessingStarted() {
|
||||||
|
liveProcessing = true;
|
||||||
|
var events = [createRandomEvent()];
|
||||||
|
self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, events)
|
||||||
|
.then(function () {
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
}
|
||||||
|
function subscriptionDropped(connection, reason, error) {
|
||||||
|
test.ok(liveEvents.length === 1, "Expecting 1 live event, got " + liveEvents.length);
|
||||||
|
test.ok(catchUpEvents.length >= 1, "Expecting at least 1 catchUp event, got " + catchUpEvents.length);
|
||||||
|
done(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
var events = [createRandomEvent()];
|
||||||
|
this.conn.appendToStream(self.testStreamName, client.expectedVersion.noStream, events)
|
||||||
|
.then(function() {
|
||||||
|
var subscription = self.conn.subscribeToStreamFrom(self.testStreamName, null, false, eventAppeared, liveProcessingStarted, subscriptionDropped);
|
||||||
|
|
||||||
|
test.areEqual("subscription.streamId", subscription.streamId, self.testStreamName);
|
||||||
|
test.areEqual("subscription.isSubscribedToAll", subscription.isSubscribedToAll, false);
|
||||||
|
test.areEqual("subscription.readBatchSize", subscription.readBatchSize, 500);
|
||||||
|
test.areEqual("subscription.maxPushQueueSize", subscription.maxPushQueueSize, 10000);
|
||||||
|
})
|
||||||
|
.catch(test.done);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
require('./common/base_test').init(module.exports);
|
56
test/subscribeToStream_test.js
Normal file
56
test/subscribeToStream_test.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
const uuid = require('uuid');
|
||||||
|
const client = require('../src/client');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'Test Subscribe To Stream Happy Path': function(test) {
|
||||||
|
const resolveLinkTos = false;
|
||||||
|
const numberOfPublishedEvents = 5;
|
||||||
|
test.expect(numberOfPublishedEvents + 6);
|
||||||
|
var publishedEvents = [];
|
||||||
|
for(var i=0;i<numberOfPublishedEvents;i++)
|
||||||
|
publishedEvents.push(client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'anEvent'));
|
||||||
|
|
||||||
|
var _doneCount = 0;
|
||||||
|
function done(err) {
|
||||||
|
test.ok(!err, err ? err.stack : '');
|
||||||
|
if (++_doneCount < 2) return;
|
||||||
|
test.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testAllPublishedEventsAppeared() {
|
||||||
|
test.areEqual("receivedEvents.length", receivedEvents.length, numberOfPublishedEvents);
|
||||||
|
}
|
||||||
|
function testEventsAppearedInCorrectOrder() {
|
||||||
|
for (var j = 0; j < numberOfPublishedEvents; j++)
|
||||||
|
test.ok(receivedEvents[j].originalEvent.eventId === publishedEvents[j].eventId,
|
||||||
|
"receivedEvents[" + j + "] != publishedEvents[" + j + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
var receivedEvents = [];
|
||||||
|
function eventAppeared(subscription, event) {
|
||||||
|
receivedEvents.push(event);
|
||||||
|
if (receivedEvents.length === numberOfPublishedEvents) subscription.close();
|
||||||
|
}
|
||||||
|
function subscriptionDropped(subscription, reason, error) {
|
||||||
|
if (error) return done(error);
|
||||||
|
testAllPublishedEventsAppeared();
|
||||||
|
testEventsAppearedInCorrectOrder();
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
var self = this;
|
||||||
|
this.conn.subscribeToStream(this.testStreamName, resolveLinkTos, eventAppeared, subscriptionDropped)
|
||||||
|
.then(function(subscription) {
|
||||||
|
test.areEqual("subscription.streamId", subscription.streamId, self.testStreamName);
|
||||||
|
test.areEqual("subscription.isSubscribedToAll", subscription.isSubscribedToAll, false);
|
||||||
|
test.areEqual("subscription.lastEventNumber", subscription.lastEventNumber, client.expectedVersion.emptyStream);
|
||||||
|
|
||||||
|
return self.conn.appendToStream(self.testStreamName, client.expectedVersion.emptyStream, publishedEvents);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(test.done)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
require('./common/base_test').init(module.exports);
|
191
test/transactions_test.js
Normal file
191
test/transactions_test.js
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
var uuid = require('uuid');
|
||||||
|
var Long = require('long');
|
||||||
|
var client = require('../src/client');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
setUp: function(cb) {
|
||||||
|
cb();
|
||||||
|
},
|
||||||
|
'Start A Transaction Happy Path': function(test) {
|
||||||
|
test.expect(1);
|
||||||
|
this.conn.startTransaction(this.testStreamName, client.expectedVersion.noStream)
|
||||||
|
.then(function(trx) {
|
||||||
|
test.ok(Long.isLong(trx.transactionId), "trx.transactionId should be a Long.");
|
||||||
|
test.done();
|
||||||
|
})
|
||||||
|
.catch(test.done);
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
'Start A Transaction With Wrong Expected Version': function(test) {
|
||||||
|
this.conn.startTransaction(this.testStreamName, 10)
|
||||||
|
.then(function(trx) {
|
||||||
|
test.fail("Start Transaction with wrong expected version succeeded.");
|
||||||
|
test.done();
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
var isWrongExpectedVersion = err instanceof client.WrongExpectedVersionError;
|
||||||
|
if (isWrongExpectedVersion) return test.done();
|
||||||
|
test.done(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'Start A Transaction With Deleted Stream': function(test) {
|
||||||
|
var self = this;
|
||||||
|
this.conn.deleteStream(this.testStreamName, client.expectedVersion.emptyStream)
|
||||||
|
.then(function() {
|
||||||
|
return self.conn.startTransaction(self.testStreamName, client.expectedVersion.any);
|
||||||
|
})
|
||||||
|
.then(function(trx) {
|
||||||
|
test.fail("Start Transaction with deleted stream succeeded.");
|
||||||
|
test.done();
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
var isStreamDeleted = err instanceof client.StreamDeletedError;
|
||||||
|
test.ok(isStreamDeleted, "Expected StreamDeletedError got " + err.constructor.name);
|
||||||
|
if (isStreamDeleted) return test.done();
|
||||||
|
test.done(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
'Start A Transaction With No Access': function(test) {
|
||||||
|
test.expect(1);
|
||||||
|
var self = this;
|
||||||
|
var metadata = {$acl: {$w: "$admins"}};
|
||||||
|
this.conn.setStreamMetadataRaw(this.testStreamName, -1, metadata)
|
||||||
|
.then(function() {
|
||||||
|
return self.conn.startTransaction(self.testStreamName, client.expectedVersion.any);
|
||||||
|
})
|
||||||
|
.then(function(trx) {
|
||||||
|
test.fail("Start Transaction with no access succeeded.");
|
||||||
|
test.done();
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
var isAccessDenied = err instanceof client.AccessDeniedError;
|
||||||
|
test.ok(isAccessDenied, "Expected AccessDeniedError got " + err.constructor.name);
|
||||||
|
if (isAccessDenied) return test.done();
|
||||||
|
test.done(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'Continue A Transaction Happy Path': function(test) {
|
||||||
|
var self = this;
|
||||||
|
this.conn.startTransaction(this.testStreamName, client.expectedVersion.emptyStream)
|
||||||
|
.then(function(trx) {
|
||||||
|
return trx.write(client.createJsonEventData(uuid.v4(), {a: Math.random()}, null, 'anEvent'))
|
||||||
|
.then(function () {
|
||||||
|
return self.conn.continueTransaction(trx.transactionId);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function(trx) {
|
||||||
|
return trx.write(client.createJsonEventData(uuid.v4(), {a: Math.random()}, null, 'anEvent'))
|
||||||
|
.then(function() {
|
||||||
|
return trx.commit();
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
test.done();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(test.done);
|
||||||
|
},
|
||||||
|
'Write/Commit Transaction Happy Path': function(test) {
|
||||||
|
test.expect(2);
|
||||||
|
var self = this;
|
||||||
|
this.conn.startTransaction(this.testStreamName, client.expectedVersion.emptyStream)
|
||||||
|
.then(function(trx) {
|
||||||
|
self.events = [];
|
||||||
|
for(var i = 0; i < 15; i++) {
|
||||||
|
var event = {a: uuid.v4(), b: Math.random()};
|
||||||
|
self.events.push(client.createJsonEventData(uuid.v4(), event, null, 'anEvent'));
|
||||||
|
}
|
||||||
|
return trx.write(self.events)
|
||||||
|
.then(function() {
|
||||||
|
var events = [];
|
||||||
|
for(var j = 0; j < 9; j++) {
|
||||||
|
var event = {a: Math.random(), b: uuid.v4()};
|
||||||
|
events.push(client.createJsonEventData(uuid.v4(), event, null, 'anotherEvent'));
|
||||||
|
}
|
||||||
|
Array.prototype.push.apply(self.events, events);
|
||||||
|
trx.write(events);
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
return trx.commit();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function(result) {
|
||||||
|
test.ok(result.logPosition, "Missing result.logPosition");
|
||||||
|
test.areEqual("result.nextExpectedVersion", result.nextExpectedVersion, self.events.length-1);
|
||||||
|
test.done();
|
||||||
|
})
|
||||||
|
.catch(test.done);
|
||||||
|
},
|
||||||
|
'Write/Commit Transaction With Wrong Expected Version': function(test) {
|
||||||
|
test.expect(1);
|
||||||
|
this.conn.startTransaction(this.testStreamName, 10)
|
||||||
|
.then(function(trx) {
|
||||||
|
return trx.write(client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'anEvent'))
|
||||||
|
.then(function() {
|
||||||
|
return trx.commit();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
test.fail("Commit on transaction with wrong expected version succeeded.");
|
||||||
|
test.done();
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
var isWrongExpectedVersion = err instanceof client.WrongExpectedVersionError;
|
||||||
|
test.ok(isWrongExpectedVersion, "Expected WrongExpectedVersionError, but got " + err.constructor.name);
|
||||||
|
if (isWrongExpectedVersion) return test.done();
|
||||||
|
test.done(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'Write/Commit Transaction With Deleted Stream': function(test) {
|
||||||
|
test.expect(1);
|
||||||
|
var self = this;
|
||||||
|
this.conn.deleteStream(this.testStreamName, client.expectedVersion.emptyStream, true)
|
||||||
|
.then(function() {
|
||||||
|
return self.conn.startTransaction(self.testStreamName, client.expectedVersion.any);
|
||||||
|
})
|
||||||
|
.then(function(trx) {
|
||||||
|
return trx.write(client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'anEvent'))
|
||||||
|
.then(function() {
|
||||||
|
return trx.commit();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
test.fail("Commit on transaction on deleted stream succeeded.");
|
||||||
|
test.done();
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
var isStreamDeleted = err instanceof client.StreamDeletedError;
|
||||||
|
test.ok(isStreamDeleted, "Expected StreamDeletedError, but got " + err.constructor.name);
|
||||||
|
if (isStreamDeleted) return test.done();
|
||||||
|
test.done(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'Write/Commit Transaction With No Write Access': function(test) {
|
||||||
|
test.expect(1);
|
||||||
|
var self = this;
|
||||||
|
this.conn.startTransaction(this.testStreamName, client.expectedVersion.any)
|
||||||
|
.then(function(trx) {
|
||||||
|
var metadata = {$acl: {$w: "$admins"}};
|
||||||
|
return self.conn.setStreamMetadataRaw(self.testStreamName, -1, metadata)
|
||||||
|
.then(function () {
|
||||||
|
return trx.write(client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'anEvent'))
|
||||||
|
.then(function () {
|
||||||
|
return trx.commit();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
test.fail("Commit on transaction on deleted stream succeeded.");
|
||||||
|
test.done();
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
var isAccessDenied = err instanceof client.AccessDeniedError;
|
||||||
|
test.ok(isAccessDenied, "Expected AccessDeniedError, but got " + err.constructor.name);
|
||||||
|
if (isAccessDenied) return test.done();
|
||||||
|
test.done(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
require('./common/base_test').init(module.exports);
|
||||||
|
|
Reference in New Issue
Block a user