Compare commits

..

90 Commits

Author SHA1 Message Date
Nicolas Dextraze
80f2e7e50f Merge branch 'release-22.10' 2023-01-30 16:17:53 -05:00
Nicolas Dextraze
9bbc961d52 version 22.10.0 2023-01-30 16:17:22 -05:00
Nicolas Dextraze
3a2e30281f Merge branch 'release-22.10' 2023-01-30 15:42:07 -05:00
Nicolas Dextraze
a48e4fa386 compatibility for 22.x 2023-01-30 15:37:10 -05:00
Nicolas Dextraze
c422057f5f start with release-21.10 code changes 2023-01-30 15:08:59 -05:00
6159cd56b8
Merge pull request #94 from jwulf/https
Support https for projections
2022-10-19 13:48:32 -04:00
cbf3c7bb5e
Update README.md 2022-10-14 11:06:04 -04:00
Nicolas Dextraze
b17ab0c77a
Update README.md 2020-12-16 11:05:13 -05:00
Nicolas Dextraze
c67d0d25a2
Merge pull request #102 from PrestaShopCorp/fixShuffle
Fix for shuffle method and remove unused code
2020-10-02 16:54:50 -07:00
maniolias
5bc2aaa093 refactor: delete unused code 2020-10-01 16:27:39 +02:00
maniolias
401f6c3190 fix(shuffle): fix shuffle methode for array with length <= 1 2020-10-01 16:23:42 +02:00
Nicolas Dextraze
40736d9da3 relese version to 0.2.17 2020-09-28 17:29:02 -07:00
Nicolas Dextraze
853ae875b8
Merge pull request #101 from PrestaShopCorp/feature/cluster
Rework on cluster discovering
2020-09-28 17:15:19 -07:00
maniolias
da6d059547 feat(cluster): create way to test single/gossip/cluster
* docker-compose files to easily setup cluster or single node eventstore
* programmatic tests for single/gossip/cluster
2020-09-17 10:33:49 +02:00
maniolias
e9834daa31 feat(tcp): add timeout when trying to connect to a TCP endpoint
* the timeout parameters in options will trigger a timeout only when the socket is connected (cf. https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback). To handle connection timeout we have to handle it manually
2020-09-17 09:52:11 +02:00
maniolias
f79a0444f6 feat(cluster): rewrite cluster discovering
* Discovering process adapted from EventStore scala client
* Use DNS for first discover but also for reconnoctions (the aim is to be able to reconnect even if all nodes have new IP addresses eg. after rolling update in a cloud environment)
* Being able to perform proper unit testing
2020-09-17 09:52:11 +02:00
Nicolas Dextraze
eb56e077f9
Merge pull request #95 from aliabbasjaffri/HA-Cluster-Connectivity-Issue
🐛 Adding object reference to externalTcpIp
2020-03-02 08:46:39 -08:00
Ali Abbas Jaffri
11f882501f 🐛 Adding object reference to externalTcpIp: Solves locating cluster via gossip seeds 2020-03-02 15:39:32 +01:00
Josh Wulf
7b7ccc2eef Correct protocol detection 2019-12-11 02:54:47 +10:00
Josh Wulf
6b75d241fc Support https for projections 2019-12-11 02:47:41 +10:00
Josh Wulf
7da398eae8 Support https for projections 2019-12-11 02:39:37 +10:00
195a6d40ce Attempt at fixing Typescript dependencies issues 2019-11-02 16:13:51 -07:00
55b9ddd856 Minor improvement to protobuf isolation 2019-11-02 16:02:07 -07:00
fe82aebfec Fix #91 2019-11-02 15:40:02 -07:00
Nicolas Dextraze
3f7798d46e Update package-lock.json and yarn.lock 2019-11-01 13:12:19 -07:00
Nicolas Dextraze
6ad2d34114 Added some more tests around RecordedEvent properties type 2019-11-01 12:57:54 -07:00
Nicolas Dextraze
67dab18d53 Fixed Buffer deprecated warnings when using node >= v10 2019-11-01 12:53:24 -07:00
Nicolas Dextraze
78677ba53e Add missing created to RecordedEvent typescript definition 2019-11-01 12:47:03 -07:00
Nicolas Dextraze
bbf25ee8b5 Update dev dependencies
Set idea language to ES6
Run tests against dist
2019-11-01 11:30:06 -07:00
Nicolas Dextraze
732b691e40 Move typescript dependencies back to dev 2019-11-01 11:13:04 -07:00
Nicolas Dextraze
941f671ed2 Fix missing warn in Loggers 2019-11-01 11:10:16 -07:00
Nicolas Dextraze
343eaa85df
Merge pull request #86 from MajorBreakfast/patch-1
Make type definition of expectedVersion match implementation
2019-11-01 10:48:57 -07:00
Josef Brandl
e0fcf9a72d
Make type definition of expectedVersion match implementation 2019-08-11 18:54:06 +02:00
Nicolas Dextraze
02642c5cfb Fix appendToStream with expectedVersion any and same event not working due to protobufjs bug 2019-08-02 22:34:38 -07:00
Nicolas Dextraze
d64f181dbb release version 0.2.12 2019-06-06 12:22:00 -07:00
Nicolas Dextraze
9e31b17c44
Merge pull request #80 from jdextraze/jdextraze/simple-queued-handler-log-issue
Fix error with logging in SimpleQueuedHandler handler
2019-06-06 12:16:53 -07:00
Jonathan Dextraze
4ae4c1e010 Fix error with logging in SimpleQueuedHandler handler 2019-06-06 11:42:22 -07:00
76a405ace2 Publish version 0.2.11
Fixing #72, #73, #74
2019-05-20 13:06:20 -07:00
Nicolas Dextraze
930b900426
Merge pull request #78 from Poimen/fix-issue-76
Adds extra options to delete
2019-05-20 13:01:26 -07:00
Nicolas Dextraze
934251d7fa
Merge pull request #77 from Poimen/fix-issue-75
Add reset endpoint to projection manager
2019-05-20 13:00:44 -07:00
Richard Shephard
1449b43b8d Fix missing = in delete uri 2019-05-17 17:48:29 +02:00
Richard Shephard
2f4850680f Fix incorrect variables used after undefined removal 2019-05-16 17:01:30 +02:00
Richard Shephard
8f68923008 Remove undefine's check as the options will need to be provided anyways... 2019-05-16 17:00:47 +02:00
Richard Shephard
8e54a22cd2 Fix uri for reset 2019-05-16 16:52:11 +02:00
Richard Shephard
6663b42903 Fix sendDelete call to provide empty data so header is correct 2019-05-16 16:18:09 +02:00
Richard Shephard
ad8d16d112 Fix documentation 2019-05-16 15:37:58 +02:00
Richard Shephard
bebd04a398 Fix client endpoint used 2019-05-16 15:36:05 +02:00
Richard Shephard
4d981596eb Fix typings for delete (there is no deleteQuery) in projection manager 2019-05-16 15:35:20 +02:00
Richard Shephard
7ccced45aa Add reset endpoint 2019-05-16 13:57:11 +02:00
Richard Shephard
88df807508 Update documentation for strict-event-emitter-types dependency 2019-05-16 11:59:56 +02:00
Richard Shephard
35f720f94f Added extra parameters to delete projections 2019-05-16 11:56:25 +02:00
Nicolas Dextraze
6d49f51e7d fix issue #74 - projections manager create continuous trackEmittedStreams parameter should be lowercase 2019-05-15 10:23:48 -07:00
Nicolas Dextraze
d8baed4c5d fix issue #73 - move types dependencies from dev dependencies so they install for dependant 2019-05-15 09:52:57 -07:00
Nicolas Dextraze
7e5327c278 Published version 0.2.10 and moved types dependencies to devDependencies 2019-02-19 09:03:17 -08:00
Nicolas Dextraze
bb53c01fe8
Merge pull request #69 from sebastianneb/68-debug
Fix for issue #68 by using self._log_debug instead of this._log.debug
2019-02-19 08:26:20 -08:00
Neb, Sebastian
20818869f1 Fix for issue #68 by using self._log_debug instead of this._log.debug 2019-02-19 09:55:49 +01:00
Nicolas Dextraze
16f172c6f2
Merge pull request #66 from Ulrikop/ts-declarations
Adds EventEmitter declarations to EventStoreNodeConnection
2019-02-18 09:16:11 -08:00
Nicolas Dextraze
7db060af6e Removed some console
Fixed issue 60
2019-02-18 09:12:28 -08:00
Nicolas Dextraze
2c272a19f5
Merge pull request #67 from JurJean/remove-subscription-started-log
Remove subscription started log
2019-02-14 20:44:20 -08:00
Jur Jean
7dc4658719 Merge branch 'master' of github.com:nicdex/node-eventstore-client into remove-subscription-started-log 2019-02-13 12:51:07 +01:00
Jur Jean
811368c1af Removed the console log about subscription started 2019-02-13 12:50:32 +01:00
Ulrikop
fcf27fba20 Changes ts declaration for EventStoreNodeConnection so that it offers all EventEmitter methods. 2019-02-09 13:16:07 +01:00
Nicolas Dextraze
e9d86a3c4d Fix issue #63 - export PersistentSubscriptionNakEventAction 2019-01-17 10:00:49 -08:00
Nicolas Dextraze
0db2b93b79
Merge pull request #62 from JurJean/invalid-missing-content-type
Send Content-Length to eliminate missing Content-Type error in some cases
2018-12-05 08:59:53 -08:00
Jur Jean
ef26ed636f Send Content-Length to eliminate missing Content-Type error in some cases 2018-12-04 14:05:44 +01:00
2893454e8a Updated messages.proto to have optional event for ResolvedEvent and ResolvedIndexedEvent and regenarated protobuf code 2018-10-27 16:00:44 -07:00
a0642ab081 Re-add optional (allowing null) on ResolvedEvent event property in protobuf 2018-10-27 15:50:11 -07:00
760d31cc31 Published 0.2.5 to npm 2018-10-21 14:55:05 -07:00
c1f17a7d99 #61 fixed subscriptionDropped being called only once
Added subscribe-all-from-events sample to test
2018-10-21 14:47:25 -07:00
96145fbe49 Published 0.2.4, adding package-lock.json 2018-09-01 14:06:24 -07:00
Nicolas Dextraze
5ac4a206ad
Merge pull request #59 from sledorze/master
Fix definitions of PersistentSubscriptions
2018-09-01 13:51:41 -07:00
Stéphane Le Dorze
e1f99cecbc
Fix definitions of PersistenSubscriptions
`userCredentials` is now correctly typed as `UserCredentials`
2018-08-21 10:17:46 +02:00
Nicolas Dextraze
0a64b37b6f Fix issue with fake-server 2018-07-11 16:10:53 -07:00
Nicolas Dextraze
3421d41068 Adding a fake server for testing purpose 2018-07-10 09:22:21 -07:00
215708014c Add Object.freeze on private enums
Improve code readability
2018-07-09 10:27:12 -07:00
Nicolas Dextraze
c3a63ff8b7
Merge pull request #58 from cdimitroulas/improve-docs
improve docs for createConnection
2018-07-09 09:12:43 -07:00
Christos Dimitroulas
d8c0bfb03a improve docs for createConnection 2018-07-09 11:58:39 +01:00
34e8262dc8 Make Ssl tests opt-in 2018-04-12 09:42:49 -07:00
bc2fbe14e3 Adding SSL support, release 0.2.3 2018-03-11 15:25:44 -07:00
9fc5d64ceb Adding start/end as static Position
Add the streamPosition constants
Move Projections into the expose classes section
Version 0.2.2
2018-02-19 10:06:33 -08:00
Nicolas Dextraze
2bdd74e041
Merge pull request #53 from paullucas/master
Upgrade jsdoc to version 3.5.5
2018-02-18 17:03:07 -08:00
Paul
fde6ad2e77 Use jsdoc version 3.5.5 2018-02-18 16:50:59 -08:00
4a2065c0a7 Release npm package version to 0.2.1 2018-01-17 11:31:25 -08:00
67dd275b5d Fix potential error handling bug due to missing EventEmitter.call(this) in EventStoreNodeConnection and EventStoreConnectionLogicHandler 2018-01-17 11:29:24 -08:00
8997a8c398 Update README 2017-11-08 13:06:16 -08:00
a5807483a2 Merge branch 'v4_proto' 2017-11-08 12:47:30 -08:00
e79ad8f9c4 Use Long in Stream catchup subscription
Use Long in tests
Bump version to 2.0
2017-11-08 12:45:22 -08:00
Nicolas Dextraze
4584517ede Update tests, typescript declaration 2017-10-17 15:55:07 -07:00
Nicolas Dextraze
e6af223f0d Fix identify client serialize 2017-10-17 14:36:00 -07:00
Nicolas Dextraze
990351738c Update proto 2017-10-17 14:31:26 -07:00
119 changed files with 20482 additions and 6102 deletions

1
.gitignore vendored
View File

@ -34,6 +34,7 @@ node_modules
# InteliJ # InteliJ
.idea/workspace.xml .idea/workspace.xml
.idea/shelf
.vscode .vscode
lib/ lib/

View File

@ -0,0 +1,11 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<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>
</code_scheme>
</component>

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="JavaScriptSettings"> <component name="JavaScriptSettings">
<option name="languageLevel" value="ES5" /> <option name="languageLevel" value="ES6" />
</component> </component>
</project> </project>

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
v14.21.2

View File

@ -1,11 +1,14 @@
# node-eventstore-client # node-eventstore-client
A port of the EventStore .Net ClientAPI to Node.js A port of the EventStore .Net ClientAPI to Node.js
## Learning
If you want to learn more about EventSourcing/EventModeling, you can join one of the monthly virtual workshops offered by my employer Adaptech Group, see info at [https://adaptechgroup.com/#workshop](https://adaptechgroup.com/#workshop).
## Status ## Status
### Missing features: ### Missing features:
- Ssl connection
- Set system settings - Set system settings
### Areas to improve ### Areas to improve
@ -21,10 +24,25 @@ A port of the EventStore .Net ClientAPI to Node.js
Install using `npm install node-eventstore-client` Install using `npm install node-eventstore-client`
Versions to install to match EventStore versions:
- 0.1.x for ES prior to 3.9.4
- 0.2.x for ES from 3.9.4 to 5.x
- 20.10.x for ES 20.10 LTS
- 21.10.x for ES 21.10 LTS
- 22.10.x for ES 22.10 LTS
### Dependencies ### Dependencies
- Node.js >= 4.0 - Node.js >= 14
- 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`) - Modules: [long](https://www.npmjs.org/package/long), [protobufjs](https://www.npmjs.org/package/protobufjs), [uuid](https://www.npmjs.org/package/uuid), [strict-event-emitter-types](https://www.npmjs.com/package/strict-event-emitter-types) (installed via `npm install`)
### Install and run an Eventstore on localhost
See https://eventstore.com
*Note: If you are using a version of EventStore prior to 3.9.4, you need to use version 0.1.x of this package `npm install node-eventstore-client@^0.1`.*
### API Documentation ### API Documentation
@ -36,10 +54,6 @@ The offline documentation can be found in the module folder `./node_modules/node
The online documentation can be found at [https://dev.nicdex.com/node-eventstore-client/docs/](https://dev.nicdex.com/node-eventstore-client/docs/) The online documentation can be found at [https://dev.nicdex.com/node-eventstore-client/docs/](https://dev.nicdex.com/node-eventstore-client/docs/)
### Install & run an Eventstore on localhost
See http://docs.geteventstore.com/introduction/3.9.0/ .
### Example: Storing an event ### Example: Storing an event
Save to ```app.js:``` Save to ```app.js:```
@ -110,14 +124,60 @@ To generate a test event, open a separate console and run:
## Running the tests ## Running the tests
### Local testing
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 run the tests it is recommended that you use an in-memory instance of the eventstore so you don't pollute your dev instance.
EventStore.ClusterNode.exe --run-projections=all --memdb EventStore.ClusterNode.exe --run-projections=all --memdb certificate-file=yourcert.pfx
or
./run-node.sh --run-projections=all --memdb certificate-file=yourcert.p12
You can also use docker-compose :
```bash
# start the single node cluster
npm run compose:single:start
# if you want to wait for the cluster to be available
npm run compose:wait
# run the tests
npm run test
# to cleanup (stop containres, delete volumes)
npm run compose:single:stop
```
For SSL setup see:
https://eventstore.org/docs/server/setting_up_ssl/
or
https://eventstore.org/docs/server/setting_up_ssl_linux/
To execute the tests suites simply run To execute the tests suites simply run
npm test npm test
### Isolated environment
To be able to run the tests for different connection types (tcp, gossip, cluster) docker-compose files are available to setup the environment and run the tests.
#### Prerequisites
* docker
* docker-compose
#### Run
To execute the tests suites for single node cluster (tcp connection) simply run
npm run test:single
To execute the tests suites for multiple nodes cluster (gossip connection) simply run
npm run test:gossip
To execute the tests suites for multiple nodes cluster (dns discovery connection) simply run
npm run test:cluster
## Porting .Net Task to Node.js ## 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. Any async commands returns a [Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise) object in replacement of .Net Task.

11
build-and-test.sh Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e
if [[ -d /var/code/src ]]; then
cp -R /var/code/{src,test,*.js,*.json} .
fi
npm i
npm run build
npm run test-debug

23
certs/ca/ca.crt Normal file
View File

@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIIDzTCCArWgAwIBAgIRAJD0c8z8ITssbEkzjx4ZmRowDQYJKoZIhvcNAQELBQAw
YjELMAkGA1UEBhMCVUsxGDAWBgNVBAoTD0V2ZW50IFN0b3JlIEx0ZDE5MDcGA1UE
AxMwRXZlbnRTdG9yZURCIENBIDkwZjQ3M2NjZmMyMTNiMmM2YzQ5MzM4ZjFlMTk5
OTFhMB4XDTIyMTAyMjEzNTA1N1oXDTI3MTAyMjEzNTA1N1owYjELMAkGA1UEBhMC
VUsxGDAWBgNVBAoTD0V2ZW50IFN0b3JlIEx0ZDE5MDcGA1UEAxMwRXZlbnRTdG9y
ZURCIENBIDkwZjQ3M2NjZmMyMTNiMmM2YzQ5MzM4ZjFlMTk5OTFhMIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA17JM4jAG4fM4dtA6mpatLVkYhS42wwHC
RbG7dQINSE9wMKpoy2u4sDvZQrsudOvdeclPkIL1JgUtk7lDlqRMXZ6HnP7zTCKn
SQCXzvc3IIU0cGlkffuIS/kuAGBePvmmTwFE3ByDneydlHpSUGEvQY1KEAKwpgSO
LX1NtthP3kKT+bq0NdQlgOjm9dEkSYWwfCM7Of9cJdh8bCO4zJ1taGqchxluWlpo
DJL6TTHsZDvDjhfzBxhjuRax4S8B8xLaq6LirTFEFGpqFBhqzGGhRl42ZylKqVo9
fKhhywwq81ItROmB14OGKa8gyJi7pWGSKRI4M6Ngn89D50Q49K1QdQIDAQABo34w
fDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBAjApBgNVHQ4EIgQg
/3vmZ5JvcSlcqyaNXgrQZ5FOXlZ9HvYr9x+Tbrs3Mx4wKwYDVR0jBCQwIoAg/3vm
Z5JvcSlcqyaNXgrQZ5FOXlZ9HvYr9x+Tbrs3Mx4wDQYJKoZIhvcNAQELBQADggEB
AIMQR/3BHnVYAuG6DSdDPVYkPR9Fvwi9G9U1nybMHY9uSnEOp6yASrRc/cD7pIbP
Ax7cvD5f0829fAUFupXtp2T/yNOA7/eis7l6Y0ZZf+QEk9XhZXHVTFLFDb9Fyc6r
+i/lXnatGsYwOns0RFSG6tzxi2lxUPwlXauacwTBW4jrphtn0vC3q/n85JOEi2ib
aIlXaXUTREJFiFnS22/SX4dSRaeimL34rrd9AjX53Ne6pUiVBGgd2aSUkBh65EYk
VCIZwuWLx0jgcT8NI/EeId7FN4CWq73dze5eY2QU00rXidyYDeC2aZcU5e2RTQqn
5dGqFGAd/xCopGYdWfusqKM=
-----END CERTIFICATE-----

27
certs/ca/ca.key Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA17JM4jAG4fM4dtA6mpatLVkYhS42wwHCRbG7dQINSE9wMKpo
y2u4sDvZQrsudOvdeclPkIL1JgUtk7lDlqRMXZ6HnP7zTCKnSQCXzvc3IIU0cGlk
ffuIS/kuAGBePvmmTwFE3ByDneydlHpSUGEvQY1KEAKwpgSOLX1NtthP3kKT+bq0
NdQlgOjm9dEkSYWwfCM7Of9cJdh8bCO4zJ1taGqchxluWlpoDJL6TTHsZDvDjhfz
BxhjuRax4S8B8xLaq6LirTFEFGpqFBhqzGGhRl42ZylKqVo9fKhhywwq81ItROmB
14OGKa8gyJi7pWGSKRI4M6Ngn89D50Q49K1QdQIDAQABAoIBAQCbeiKBSC0t4avp
uN8FX2clMPvDxDQlDLeElUCSbxAz907fRXaUaumN3xYUS+iIzxuuFbYMbNlYtpLL
0NcvVu7U7NjECeJGFxSOQqcqwHg0MNBD1zRgu1bbrJnEG7LTohCHElsYblVRGKXd
yO5ckjEGB1pDgCzk/pOdppzGF9Y6Vl/6WfZQgdGV6B1Qh9Vxs+1CGbK/6YqMM3av
9AJTEXgXbHOPyqNwixjxihpz79hXx0lOznL/CeosHKj7+ZY/knWZcDBmNq3ZA3Nz
w3p0fDkBtY7zQglh5nTSYoLtpcJvzkAI8uD6eJ4ThwIGUbLlvq0c3yaHZwIqrmEq
py09FA8BAoGBAPnuy+ehTBXV5k8icPZjI50Bjr5Alres01OjHxAA5bZJACwVbjMQ
a2464yb6DMA6NDrUevfHvKn3rw8OoceNrlaEua0qE5IweRpROf+ENqW9Yhcg4O1G
bSUPf5M1JusCSznAJu+gKsqHq0pTtdssh8Xb655xgd3gNMi0lLPlw5+tAoGBANzu
vidC8LRH+4JRrb+7PUZi1AR8vtCpabkLuVe7AZJl1wXFvYJTyFcW/e0WTvETcclf
9dhy1c0lH44571L4kpKRRKF4U3lcESbiLPDwjvfnls1rYzfzjY1YWoERwGYxL54H
2BBYPjKocnHZOnyiBxYn4ysK2O3dYlFxgJy/1GzpAoGBAM4HzW1HqRoC91Va1fY2
aaA/LXWmdtg5DwhOWwftRXAMI6kzjWFo8rauspxp2uMH2BB7Ildp/xu6A0SNUk3X
fNTzmsCmA/YqIVrRhNcfHZFpWhgo0WKoAMdhTFCEq21lua+EuFQnG79TMaAW+74O
qmovzb8SYPUPo1OkkUTvbmENAoGAODTg2Zo1JJhfiyL+ghW/nAWOjOmzneBLzlEG
QSSJXNZd4tEQynRINqfZyBRx1vEsdJ/eKsfkTsGQCp4AneoxV+pvPGkhS3G5x1up
NU7uAHHBstcMMfLbY0dEFywN97GxvJGgTWA1yquCGia6s3SBOwjAyXWEBNfaqH+o
ybgf8ekCgYEA3/Vx/CZ/VB0Fr47cpZGckJWH6lDWkzBEcaAT22VYlhL4CZ0CRDWj
6+LSiQDuI3+JwEhZlIkTvfZSo5bhNeUHoEL+15yg2RvsWFzorDxBfvZ//29BQg/Q
bN+vlKHpn7V7T9LQg/Tp107mfW6Z/TG4UYIVJyxY/JUjXjQLcFPvTVE=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDSzCCAjMCFAxEesn/oIrU2T7DOU9cWCyoCCqfMA0GCSqGSIb3DQEBCwUAMGIx
CzAJBgNVBAYTAkNBMQswCQYDVQQIDAJCQzESMBAGA1UEBwwJVmFuY291dmVyMRcw
FQYDVQQKDA5BZGFwdGVjaCBHcm91cDEZMBcGA1UEAwwQZXZlbnRzdG9yZS5sb2Nh
bDAeFw0yMTA1MDYyMDAyMzZaFw0zMTA1MDQyMDAyMzZaMGIxCzAJBgNVBAYTAkNB
MQswCQYDVQQIDAJCQzESMBAGA1UEBwwJVmFuY291dmVyMRcwFQYDVQQKDA5BZGFw
dGVjaCBHcm91cDEZMBcGA1UEAwwQZXZlbnRzdG9yZS5sb2NhbDCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBANbNrVQ91TFVsrfFV39/1MjxM/UIP78FaX+S
kxAzcpAJKP9PbHLwFXxkl0Bam+3ekE6+hc3qT8o4ksOdzwz+dJbdZF8gblLda7vq
JnPjkAZuhojRLPfXvaJZbManEkB30+JIvb/XWhMf34+kvyZaOnKqidzNAuLAFQ+6
KCCXYnTUFimumr6AsrZvA9UzN3edrk1pqxMYNT+8MBOUnNfkdHX78aTzdWvq4+Ro
+58MT3NpqdI+1cY423JlG2qj8PAqPsgdE4XvmtqDtLndH+VflR1M1Rkn41cofgSh
lo7PE2SNn4hSv8m+1HJMfcegJEXKE3LIGHJtV6qT6S+pRtJd9mcCAwEAATANBgkq
hkiG9w0BAQsFAAOCAQEAlhKziHmYm81vrx+EHqIZXvmoUp3I1A9STvFqUdHrv9sa
8tKD7wW0owTVaxXBW3380ZFRpG3JVJFUgOyFIhi4IccWoqVsBCE0BBR96tlRXrKT
UZTAHLEk/SdLL+qfhKgY7F7+M9Wtf5HldiRfNoTqYn6YVXMJMgcwehhHho/fCHzj
tzOMfKFjGAq+n8cScLrTaW92JbbxjFPJJ7uQDH4OjAHx6pT885+RJf0rNqboAErA
lRLcEjtihslWKD7nelOlJE1egkum/m7MLVFpZPqEBp+x1zXdwacENwIhiRrQQhKi
8Qw36BuSCZ3CZK18kGyhtxl/uUP/wx7XXc1U0W2Wtw==
-----END CERTIFICATE-----

BIN
certs/eventstore.p12 Normal file

Binary file not shown.

24
certs/node1/node1.crt Normal file
View File

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIECDCCAvCgAwIBAgIQI83qjs8Vn2iLbeUZbTPRcTANBgkqhkiG9w0BAQsFADBi
MQswCQYDVQQGEwJVSzEYMBYGA1UEChMPRXZlbnQgU3RvcmUgTHRkMTkwNwYDVQQD
EzBFdmVudFN0b3JlREIgQ0EgOTBmNDczY2NmYzIxM2IyYzZjNDkzMzhmMWUxOTk5
MWEwHhcNMjMwMTMwMTUyMzM0WhcNMjQwMTMwMTUyMzM0WjAcMRowGAYDVQQDExFl
dmVudHN0b3JlZGItbm9kZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AL3CBeNLxZDvUA4WMRACrM+kxvrlD8ahs9MOFfAJHCNLoNR4mWAvh+BUDHsG7E56
4I5Y7XbC0cCIIER3/QB6Jz80kcJ4y745d9RCbYSqTeUnptrLdcXDNiwbI3bAjyuJ
tEqg+iPjrz2E6bSlZ4GSxKSTC95Dm80UUU/CG6+H1FesN8JP+ruYey4S5bYASy6o
z1NtYaPDGqSqDGvLhuWQNKoJAAc4g1R/UcVFbuI2asnCkFGjDF7gcRKkHpw0DNhN
TMQWMdQ2+I8it5UwNzYpsCZj/le5a54mNZqNYj52nEpURDsdJy4BXJy5dUwADZHz
ffN+ciWEToQz2hZmnliIG98CAwEAAaOB/zCB/DAOBgNVHQ8BAf8EBAMCBaAwHQYD
VR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwKQYDVR0O
BCIEIFRmt7SsPXFStSAcZDS9OCP03JyevPMV6/1R0Utg8UA3MCsGA1UdIwQkMCKA
IP975meSb3EpXKsmjV4K0GeRTl5WfR72K/cfk267NzMeMGUGA1UdEQReMFyCKmNs
dXN0ZXIudGVzdHMubm9kZS1ldmVudHN0b3JlLWNsaWVudC5sb2NhbIIobm9kZTEu
dGVzdHMubm9kZS1ldmVudHN0b3JlLWNsaWVudC5sb2NhbIcEwKghCjANBgkqhkiG
9w0BAQsFAAOCAQEATHxhyf27pPFJRegPB9ozm4P06MkvZMm4syxmoR1jJohm+7Xp
7jd9ENATFApZNxV+/MYGgryML9Y+Md/Bd1fttP8j0MUR5euJX8zehrnCufaIw2If
/a9YalJ2/a+WElAgTHM1sCpdvL+WwsQ6IjWPVfGIu7VIotQXIqllZUjMd3rFI4Ip
9CkMgXbZzls8OhWH1FnPkFFjolqITwcgSmn3NWbQkl6qFYFjAZ8pFcjhdNay7yZx
+Pe9gEPbi6iyP50xb0LQtJXQQvFhwmDQbR5v0xiBI6ONK8mha3fQkxmV1wMPJdvQ
TOva/iFP4qmjyewyzOd752DN6r4qFlpb39Hpmw==
-----END CERTIFICATE-----

27
certs/node1/node1.key Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAvcIF40vFkO9QDhYxEAKsz6TG+uUPxqGz0w4V8AkcI0ug1HiZ
YC+H4FQMewbsTnrgjljtdsLRwIggRHf9AHonPzSRwnjLvjl31EJthKpN5Sem2st1
xcM2LBsjdsCPK4m0SqD6I+OvPYTptKVngZLEpJML3kObzRRRT8Ibr4fUV6w3wk/6
u5h7LhLltgBLLqjPU21ho8MapKoMa8uG5ZA0qgkABziDVH9RxUVu4jZqycKQUaMM
XuBxEqQenDQM2E1MxBYx1Db4jyK3lTA3NimwJmP+V7lrniY1mo1iPnacSlREOx0n
LgFcnLl1TAANkfN9835yJYROhDPaFmaeWIgb3wIDAQABAoIBAFO5a8eiAwWz0Wql
t/gMdhsX5QOGVbOaXTFeIafLHTMlh0y3+VRLJHSCIQ+XOmRWKwYjjoK76rg8V6Be
IK85Nl/Y3gckLSE9UfXBaXkDe2MEX8ZhxxD+1MQZJUicJY9cLsROyo9TQlqubj9a
S5UMIN9MUgw7YF7X/Bho2LZWBAa6Hz3LSyndwArvqZeUf8ovmJnWEUvJB8amVhwB
DoYqyNjiqIEJYWRfCeGfLEOmotmwCfYeJFyr84r3wfS6qmaesKM8Nbz8eSSNg7n3
5LcAL2Bv3hvsMmUEZQl3J41AtBVsU5DgbTf6/seuKvfVOcqVp9AtZp2rg3xY4pNI
7gN4wiECgYEA1lBh13LdjM0rOg9096ee2hQKjFvwVki0xzZigmf94CXoAJ19qwOe
J0WXfk1g5GP1bg5pdVhKdS+LEIqet9ipaall5hWotaYJdyOtWiQan9wRRa3sk6pl
K29LBF+XzZWAKE7C2371rVyMXViHzv056wasw0RSgxd6HBi1mOpcveMCgYEA4qrj
Ha24Qh+/b883FaMphYrl8H8nMgAdGmi7HiLnJx6jzPzalsBI5+qnDY8/oqeAie66
k8c6uRKOsEFrsbK/w3+21rGuZb9t9PupDONFOW9D94744HDFu0Clnr5S2ABKM9D6
hUL2qQedAqZx5CNWbpM/wdV0OBKhveIb9OyjytUCgYA21jCC0ThRK7owy/2w1Kn3
wQMF8Qxo94eSJZ9FINi7/tcDOUkLdgyEnfDqd2AgcFc/Rt7DEMlUfMrHe8Cq5/xJ
NfgFzGrzt71aR722f+B0IPomax9KlWH3oC2Au/j6uJMnPSfsw+qW5kRgKiPt2at1
MKsJ97RpKJutDsGAlA8Q4wKBgQCUyjBD7yEg5s43iwJRxcUf0EmYUR9dhw2TpGQQ
KEEsQMStbt3eT2KMOlBmaj4Pe3y5DmiTURiPHEtmD8ut3fRbVJ6k2AXu7HY1dm1F
9HogdZVcEBH24pAplwog5jZja+1Jjkik6YwY8NJ0RuZEJ544/5e+mWus0y4xAxfu
8teh8QKBgQDQm6vKAsldQStgj9gwRiTAUrMPTriVQTUd4/8lof+RoIJWM7CARLos
9MYkAefv4MgjvwWNn8RbFjN91NBX4qpCjgm62P25npFSKQAK+JQlvj7MbSw54jyx
2/uj4JEMM1rOocJr9tvYAeC2nnZMOuhesLVy+SHF9TJ2WtalDOTZ1A==
-----END RSA PRIVATE KEY-----

24
certs/node2/node2.crt Normal file
View File

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIECDCCAvCgAwIBAgIQdipz5iGZgwtJYjBK4mjFijANBgkqhkiG9w0BAQsFADBi
MQswCQYDVQQGEwJVSzEYMBYGA1UEChMPRXZlbnQgU3RvcmUgTHRkMTkwNwYDVQQD
EzBFdmVudFN0b3JlREIgQ0EgOTBmNDczY2NmYzIxM2IyYzZjNDkzMzhmMWUxOTk5
MWEwHhcNMjMwMTMwMTUyMzQwWhcNMjQwMTMwMTUyMzQwWjAcMRowGAYDVQQDExFl
dmVudHN0b3JlZGItbm9kZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AOJZaF5skFJeNOgkqG8B38lV6lhKwvrmspxZvw7tP8dhzV7R+EUdTXYS86ximW4c
cdK5zqXRgZG6WaaFFN4BeCLqum+602l9Bw88K5Ww68v5R9VP8sgt7w4UsqvVjTJ4
60R59PvfrwkIL1DFkKBM4B7CmdLR20VZiJQ9YghR03TBbjjvIhaDyprmxu0vDsF7
f3zNmcnBF+14Z0b/exevtiVbyvNQ6nQENSo7Whz/kDV1a2i/BsVlsbFc43ywmsHL
Ks1z2rSctTgfYmK/yUFaR4B1mkzhQf9/CfJxJQLekC9Zv85ah9tMGwl/pbDfZh7q
NUJQHjJMm4I/HeeTQPd1CXcCAwEAAaOB/zCB/DAOBgNVHQ8BAf8EBAMCBaAwHQYD
VR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwKQYDVR0O
BCIEIDlz3IFnAja157ly4cFxgZTfYrwlqAhgtbEu4TVhO/yPMCsGA1UdIwQkMCKA
IP975meSb3EpXKsmjV4K0GeRTl5WfR72K/cfk267NzMeMGUGA1UdEQReMFyCKmNs
dXN0ZXIudGVzdHMubm9kZS1ldmVudHN0b3JlLWNsaWVudC5sb2NhbIIobm9kZTIu
dGVzdHMubm9kZS1ldmVudHN0b3JlLWNsaWVudC5sb2NhbIcEwKghCzANBgkqhkiG
9w0BAQsFAAOCAQEAvj0ZjZkKdqwVdfXJAhMPGZwMf8gfZRpSCJe95FL0yePUk/jm
di2qk2sb2+W9prPR1Nbs1hfLnHHBw32yrxekvFSUC6Qc23X8S9QU01FOO7uEVtnM
fdEzyuZcdPBK2SAL/CxN0WH/yUxNWkWnqQKegZxkvpMVxMAsA0l3twWwfkEQtjhA
V/dcS5WwZxyyvCQ0qAVZ4+Zd/huvmsNIaXplvCs/P8yU6SR0TUdahJpfxJeKkKIf
k2h2/G36i26lTTm2TyxO4A2v8PotRO0xhjJkVKVEs4ZuY+lK6JUoTaM9Z8FubDqE
K2KH+YCBdER5jQK2YbmZDRh58aKMpCXtCx8RqQ==
-----END CERTIFICATE-----

27
certs/node2/node2.key Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA4lloXmyQUl406CSobwHfyVXqWErC+uaynFm/Du0/x2HNXtH4
RR1NdhLzrGKZbhxx0rnOpdGBkbpZpoUU3gF4Iuq6b7rTaX0HDzwrlbDry/lH1U/y
yC3vDhSyq9WNMnjrRHn0+9+vCQgvUMWQoEzgHsKZ0tHbRVmIlD1iCFHTdMFuOO8i
FoPKmubG7S8OwXt/fM2ZycEX7XhnRv97F6+2JVvK81DqdAQ1KjtaHP+QNXVraL8G
xWWxsVzjfLCawcsqzXPatJy1OB9iYr/JQVpHgHWaTOFB/38J8nElAt6QL1m/zlqH
20wbCX+lsN9mHuo1QlAeMkybgj8d55NA93UJdwIDAQABAoIBAQDR3UvjoIz/kdFE
HBj/M6GmLz6ktkyqDRQ12xBGarrjxk6AR/YFPvH/M/syLglMCpuOYjCzldiooTtW
JbX/USVuWmyaA4LZH89X1NYkaHF4IZz0rrfP2qb3S0GOCOZtkaexSdJXZkIIAcq8
/TDnL9/tJUkyKtk/J5B2waMPS67fZGoZ6wx3sHZ7/CCcvdtHSvCT5+taVoHGUDPi
kToGcJAIDKAOakZfv1bfzYFyln18NA55V+133tGfZYJrILmEwLcU2Y7BAaoD+yme
+BWU3eKgYRCsvA+HzdgDetEaqp78Ax/G29OUK9NWdXk2hwhmkpZgSLV3g7OlfNve
6pmuEpcBAoGBAOPIMM+uYQJmRIapXZADQOc7VkF55sSjh3h25tjS8iHIEGQ9gb8n
h4iNUDC73NN9Yfw6/vKrwcpIq5nQKRRUQ+NJFxurtN6XBBlYNf336tg6S/65oe3W
R3m35/w72qcct1aOASDafuxdoEV/kbflpao57PxRZAXGdMPJANDckujXAoGBAP5j
x4NZvYI73oL2OI/2CJytA5uksfsh0ymusNKRaBhqLwFIE1Ud/CHj5eG1Bo9ox8Yl
e5AdkMmSSYFIe3B/P1apFI22/GKc1on2mFXKRY8zAMVLGbsLJarQz4xIX36bafUh
1O9JM1d6PWDITAzoptIJDF7U41jxbbGMic6gIbBhAoGAMOUkNTCFenJhe1aX0vYf
1IMUPPHh64v+5IkBfMGrYD0F576cP2BI47MR56Qr/uoHQpGXcJgV/UokK9xXNRnx
iLSD1VjCdpWMN7SLwnyZWNkKvx8x90/r/+DOV4KxqPWtFVNeQqMkY7ClWsXLjeIp
8aSFjpQaGHNFVVGsbykqD/kCgYEA3BZ/eoidiJs7e5qCfSqjcMnPIU1wHpG5G4rj
uInMwbK8UCOHdlCm3rtJG+l+b3x7ulabfKfXqE1EAvIuubmdluBZw/HYkYiXpG0Q
hsilPasms4qyDD0srYhSyE+lLuhO16IZk5yFKt27VIx2H4T5LyZc6X7N4xmdicrJ
P1cTToECgYEAlW2xWDl90suSAw1Tv23BVEX6ol/UColtGY8VDIHsXPDBYImtGiFt
MibOuyzWsvPw0HZDzWw9otU0735clhK34v+E1WyPFvOAo18orcCS4A6Fi42tp5oG
sNVxiYJFFUn+nnHUoGZNuKlB5PZR1Wxus6wXY2rZdoQ6QsBD0XjquCE=
-----END RSA PRIVATE KEY-----

24
certs/node3/node3.crt Normal file
View File

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIECDCCAvCgAwIBAgIQecOoexYVeIUtLwNQp179vzANBgkqhkiG9w0BAQsFADBi
MQswCQYDVQQGEwJVSzEYMBYGA1UEChMPRXZlbnQgU3RvcmUgTHRkMTkwNwYDVQQD
EzBFdmVudFN0b3JlREIgQ0EgOTBmNDczY2NmYzIxM2IyYzZjNDkzMzhmMWUxOTk5
MWEwHhcNMjMwMTMwMTUyMzQ2WhcNMjQwMTMwMTUyMzQ2WjAcMRowGAYDVQQDExFl
dmVudHN0b3JlZGItbm9kZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN8iXoWZjjxOqJJLTHmHOy89lbk2pBo/ca7qPZ/WC+I52A4Lgw58/O7tV/RHFyao
X8lS2hAVMlRfToUqj7ILAVBeF3t827W0qttbv+wdnxvIWqpq1HdtV5xgLIUXbyiQ
SI97Tgn7zDJOvm41sHV0xsPWKlritliDByXlGMOabzV53yxa1c0cU2bP5n7nc9wW
KOH03OU64qCj6MJTdi35peKDhaAcRtGmr5ZggUCP4n9GUzJ1FATAkcrLMOjoh1v1
K6auqOQZ8f+ns+3bQ4G96rZxwwrSzI/leUFY4u/lzjLUlEuLALJquPrB6RiwzTwb
BOaTYG6mgWORWPBupjpj67cCAwEAAaOB/zCB/DAOBgNVHQ8BAf8EBAMCBaAwHQYD
VR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwKQYDVR0O
BCIEIMdKDLFiWvROUmnmDVing8S3lLisBr+vfrrM+A0t3yycMCsGA1UdIwQkMCKA
IP975meSb3EpXKsmjV4K0GeRTl5WfR72K/cfk267NzMeMGUGA1UdEQReMFyCKmNs
dXN0ZXIudGVzdHMubm9kZS1ldmVudHN0b3JlLWNsaWVudC5sb2NhbIIobm9kZTMu
dGVzdHMubm9kZS1ldmVudHN0b3JlLWNsaWVudC5sb2NhbIcEwKghDDANBgkqhkiG
9w0BAQsFAAOCAQEAEJmQloXSfxqWGIdGFi7UFDNr3oTxuQsEJRztKzA1rRPbvFhp
jJ1uPZKRv0SBnmaQa6+PW1zphP+kklmMq9XqiFqq7D7FmYgr2err08DbQfMxyEhG
yHmaBFzEyI/rm8jRn3K5DMV6cDCB3jTn5eud3oVVAZwTsLcR3mB2UpSJ7lAJgRFs
5pfFFgHKCQ4YQcFIkNtMeda7KpVv4YxoltJMSISDGvo4fcNhCH62H6/PDJFBi/e9
L4vB7eZHaO//8UQQxV24VSDLDBP1MQ4ucLZEVam9HQj4qK+DiwDlH6WmQs22FqqE
i7aLjEEu9Ko2zG7hzTp1jxDfyPw5iByax1JdXw==
-----END CERTIFICATE-----

27
certs/node3/node3.key Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA3yJehZmOPE6okktMeYc7Lz2VuTakGj9xruo9n9YL4jnYDguD
Dnz87u1X9EcXJqhfyVLaEBUyVF9OhSqPsgsBUF4Xe3zbtbSq21u/7B2fG8haqmrU
d21XnGAshRdvKJBIj3tOCfvMMk6+bjWwdXTGw9YqWuK2WIMHJeUYw5pvNXnfLFrV
zRxTZs/mfudz3BYo4fTc5TrioKPowlN2Lfml4oOFoBxG0aavlmCBQI/if0ZTMnUU
BMCRyssw6OiHW/Urpq6o5Bnx/6ez7dtDgb3qtnHDCtLMj+V5QVji7+XOMtSUS4sA
smq4+sHpGLDNPBsE5pNgbqaBY5FY8G6mOmPrtwIDAQABAoIBADk571QEVd5RrhoX
Bb4FWMB84vFleiDhNJ7PpI5hrxqOC0NO4IXOuG/unygfM2jOtgJEJXkKdqX8635r
s/7No8Cg4RTiqNapR0iK92XvEudlMxuR2nvmpxmL8N8Ra4jTuEhUL2eQFIaye1R4
oJD/Khr/yk0U5/87W5H0d61y5iKobF87QbI9xxkBGGQygsvoEni4sHchN1se/Ha8
y9nb6B/9sYU8lfSSaRhS/sqpH/ppnptWr1hwxj6jxI21uMB+cWN+rs8j1F0Zizex
+TTDqhyuYoqh1x2J9UFCjipiKVnA1563eFmtQZk2nrNdjvN6K9qWa0iF8IgTGvdh
m+9NNEECgYEA6sYqugSzeVCqBqHKAQKESsO6XOQjwkDZQhgeDG8Sal5RArlNsup6
hh/ZONPCL/6zxz5oKEyxzsTg4tJI9F3uqkpF5rcM1UGCc3maywXmLUd70NuTWM2G
hz+R4IhKyuQsFhWzFRBh10rorSvjIflkKiviZp+lNiDp1UnY0pOhxWUCgYEA807M
i60fuTTZsvGKhExORHUOnfs3USst1zHODDAaQYyjUYJP7ix/azf1ciHeeNCZ9rzF
9xBlDKChRSvKPnU0K07C49a+WI4rdgw8zS+8dqKdvaczPas0InPzXs+oW/O6I86I
ANEanw9fgrAPEL4/8TLMLhjTV3msdrJ04KzVWOsCgYEAtc9Ax01cr1YIVZp1FNMe
H8fzSnxiNMyDcuu6WWaqnDTuB234/dUXjUacsaVLfF+CWYm6n3rFN5q/jOfMF3Gm
obli6IhZJ8zTeRlzBkZLOkVhXRytvtdqltJj5b2mLfkFKxUi2e0V1+PqzDBwdJ7t
MrtJWK/JAWH7s3C8uFYbsQECgYEAsxD2d4H1zA/6fLlKEa47GCTymhCumJ1nwm4U
UGICoOWJuU5n10mGiXmIufP1UQNtnOs342p8hDugFVFyKl+GAq2Xv6lnM7OGhv3g
rb6JPOLXpyHsE2HyH70oi0nXhb0t7HZvH/LFXfUpfPxoShFK7BBPt0lsGYAiDsWu
QUDST/sCgYAVljJlvlEx8C8HmBrSeeh+Zec9t9HDjl04jNAwenY4x5hD1l1u2ij5
OJqVodZd1s/To7Dm9Xc6w7r6c9x8xsok4Kc3izc/KQXlxPvRnxF76J4KsMw8kEU1
LnQN+igffijohdln2Q8cQnEZylM1N4e+sNat5ufEzTk7MBn1676aHA==
-----END RSA PRIVATE KEY-----

134
docker-compose-cluster.yaml Normal file
View File

@ -0,0 +1,134 @@
version: '3.7'
services:
eventstore1:
image: eventstore/eventstore:22.10.0-buster-slim
environment:
- EVENTSTORE_CLUSTER_SIZE=3
- EVENTSTORE_CLUSTER_DNS=cluster.tests.node-eventstore-client.local
- EVENTSTORE_ADVERTISE_HOST_TO_CLIENT_AS=node1.tests.node-eventstore-client.local
- EVENTSTORE_ADVERTISE_HTTP_PORT_TO_CLIENT_AS=2113
- EVENTSTORE_ADVERTISE_TCP_PORT_TO_CLIENT_AS=1113
# - EVENTSTORE_DISCOVER_VIA_DNS=False
# - EVENTSTORE_GOSSIP_SEED=192.168.33.10:2113,192.168.33.11:2113,192.168.33.12:2113
- EVENTSTORE_RUN_PROJECTIONS=All
- EVENTSTORE_START_STANDARD_PROJECTIONS=True
- EVENTSTORE_ENABLE_EXTERNAL_TCP=true
- EVENTSTORE_EXT_TCP_PORT=1113
- EVENTSTORE_HTTP_PORT=2113
- EVENTSTORE_INT_IP=192.168.33.10
- EVENTSTORE_EXT_IP=192.168.33.10
- EVENTSTORE_CERTIFICATE_FILE=/etc/eventstore/certs/node1/node1.crt
- EVENTSTORE_CERTIFICATE_PRIVATE_KEY_FILE=/etc/eventstore/certs/node1/node1.key
- EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH=/etc/eventstore/certs/ca
expose:
- "1112"
- "1113"
- "2113"
ports:
- "1112:1112"
- "1113:1113"
- "2113:2113"
networks:
app_net:
aliases:
- cluster.tests.node-eventstore-client.local
- node1.tests.node-eventstore-client.local
ipv4_address: 192.168.33.10
volumes:
- eventstore1-data:/var/lib/eventstore
- ./certs:/etc/eventstore/certs:ro
eventstore2:
image: eventstore/eventstore:22.10.0-buster-slim
environment:
- EVENTSTORE_CLUSTER_SIZE=3
- EVENTSTORE_CLUSTER_DNS=cluster.tests.node-eventstore-client.local
- EVENTSTORE_ADVERTISE_HOST_TO_CLIENT_AS=node2.tests.node-eventstore-client.local
- EVENTSTORE_ADVERTISE_HTTP_PORT_TO_CLIENT_AS=2113
- EVENTSTORE_ADVERTISE_TCP_PORT_TO_CLIENT_AS=1113
# - EVENTSTORE_DISCOVER_VIA_DNS=False
# - EVENTSTORE_GOSSIP_SEED=192.168.33.10:2113,192.168.33.11:2113,192.168.33.12:2113
- EVENTSTORE_RUN_PROJECTIONS=All
- EVENTSTORE_START_STANDARD_PROJECTIONS=True
- EVENTSTORE_ENABLE_EXTERNAL_TCP=true
- EVENTSTORE_EXT_TCP_PORT=1113
- EVENTSTORE_HTTP_PORT=2113
- EVENTSTORE_INT_IP=192.168.33.11
- EVENTSTORE_EXT_IP=192.168.33.11
- EVENTSTORE_CERTIFICATE_FILE=/etc/eventstore/certs/node2/node2.crt
- EVENTSTORE_CERTIFICATE_PRIVATE_KEY_FILE=/etc/eventstore/certs/node2/node2.key
- EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH=/etc/eventstore/certs/ca
expose:
- "1113"
- "1112"
- "2113"
networks:
app_net:
aliases:
- cluster.tests.node-eventstore-client.local
- node2.tests.node-eventstore-client.local
ipv4_address: 192.168.33.11
volumes:
- eventstore2-data:/var/lib/eventstore
- ./certs:/etc/eventstore/certs:ro
eventstore3:
image: eventstore/eventstore:22.10.0-buster-slim
environment:
- EVENTSTORE_CLUSTER_SIZE=3
- EVENTSTORE_CLUSTER_DNS=cluster.tests.node-eventstore-client.local
- EVENTSTORE_ADVERTISE_HOST_TO_CLIENT_AS=node3.tests.node-eventstore-client.local
- EVENTSTORE_ADVERTISE_HTTP_PORT_TO_CLIENT_AS=2113
- EVENTSTORE_ADVERTISE_TCP_PORT_TO_CLIENT_AS=1113
# - EVENTSTORE_DISCOVER_VIA_DNS=False
# - EVENTSTORE_GOSSIP_SEED=192.168.33.10:2113,192.168.33.11:2113,192.168.33.12:2113
- EVENTSTORE_RUN_PROJECTIONS=All
- EVENTSTORE_START_STANDARD_PROJECTIONS=True
- EVENTSTORE_ENABLE_EXTERNAL_TCP=true
- EVENTSTORE_EXT_TCP_PORT=1113
- EVENTSTORE_HTTP_PORT=2113
- EVENTSTORE_INT_IP=192.168.33.12
- EVENTSTORE_EXT_IP=192.168.33.12
- EVENTSTORE_CERTIFICATE_FILE=/etc/eventstore/certs/node3/node3.crt
- EVENTSTORE_CERTIFICATE_PRIVATE_KEY_FILE=/etc/eventstore/certs/node3/node3.key
- EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH=/etc/eventstore/certs/ca
expose:
- "1113"
- "1112"
- "2113"
networks:
app_net:
aliases:
- cluster.tests.node-eventstore-client.local
- node3.tests.node-eventstore-client.local
ipv4_address: 192.168.33.12
volumes:
- eventstore3-data:/var/lib/eventstore-data
- ./certs:/etc/eventstore/certs:ro
nodejs:
image: node:14
working_dir: /var/build
volumes:
- ./:/var/code:ro
- ./build-and-test.sh:/var/build/build-and-test.sh:ro
- ./certs:/var/certs:ro
environment:
- NODE_EXTRA_CA_CERTS=/var/certs/ca/ca.crt
command: bash -c "tail -f /dev/null"
networks:
app_net:
ipv4_address: 192.168.33.5
volumes:
eventstore1-data:
eventstore2-data:
eventstore3-data:
networks:
app_net:
ipam:
driver: default
config:
- subnet: "192.168.33.0/24"

View File

@ -0,0 +1,54 @@
version: '3.4'
services:
eventstore:
image: eventstore/eventstore:22.10.0-buster-slim
environment:
- EVENTSTORE_CLUSTER_SIZE=1
- EVENTSTORE_RUN_PROJECTIONS=All
- EVENTSTORE_START_STANDARD_PROJECTIONS=True
- EVENTSTORE_ENABLE_EXTERNAL_TCP=true
- EVENTSTORE_EXT_TCP_PORT=1113
- EVENTSTORE_HTTP_PORT=2113
- EVENTSTORE_INT_IP=192.168.33.10
- EVENTSTORE_EXT_IP=192.168.33.10
- EVENTSTORE_CERTIFICATE_FILE=/etc/eventstore/certs/node1/node1.crt
- EVENTSTORE_CERTIFICATE_PRIVATE_KEY_FILE=/etc/eventstore/certs/node1/node1.key
- EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH=/etc/eventstore/certs/ca
ports:
- "1113:1113"
- "2113:2113"
tmpfs:
- /var/log/eventstore
volumes:
- eventstore-data:/var/lib/eventstore
- ./certs:/etc/eventstore/certs:ro
networks:
app_net:
aliases:
- node1.tests.node-eventstore-client.local
ipv4_address: 192.168.33.10
nodejs:
image: node:14
working_dir: /var/build
volumes:
- ./:/var/code:ro
- ./build-and-test.sh:/var/build/build-and-test.sh:ro
- ./certs:/var/certs:ro
environment:
- NODE_EXTRA_CA_CERTS=/var/certs/ca/ca.crt
command: bash -c "tail -f /dev/null"
networks:
app_net:
ipv4_address: 192.168.33.5
volumes:
eventstore-data:
networks:
app_net:
ipam:
driver: default
config:
- subnet: "192.168.33.0/24"

207
index.d.ts vendored
View File

@ -1,14 +1,18 @@
/// <reference types="node" /> /// <reference types="node" />
/// <reference types="Long" /> /// <reference types="Long" />
import { EventEmitter } from 'events';
import { StrictEventEmitter } from 'strict-event-emitter-types';
// Expose classes
export class Position { export class Position {
constructor(commitPosition: number|Long, preparePosition: number|Long); constructor(commitPosition: number|Long, preparePosition: number|Long);
readonly commitPosition: Long; readonly commitPosition: Long;
readonly preparePosition: Long; readonly preparePosition: Long;
static readonly start: number;
static readonly end: number;
} }
// Expose classes
export class UserCredentials { export class UserCredentials {
constructor(username: string, password: string); constructor(username: string, password: string);
readonly username: string; readonly username: string;
@ -16,7 +20,7 @@ export class UserCredentials {
} }
export class PersistentSubscriptionSettings { export class PersistentSubscriptionSettings {
constructor(resolveLinkTos: boolean, startFrom: number, extraStatistics: boolean, messageTimeout: number, constructor(resolveLinkTos: boolean, startFrom: Long|number, extraStatistics: boolean, messageTimeout: number,
maxRetryCount: number, liveBufferSize: number, readBatchSize: number, historyBufferSize: number, maxRetryCount: number, liveBufferSize: number, readBatchSize: number, historyBufferSize: number,
checkPointAfter: number, minCheckPointCount: number, maxCheckPointCount: number, checkPointAfter: number, minCheckPointCount: number, maxCheckPointCount: number,
maxSubscriberCount: number, namedConsumerStrategy: string) maxSubscriberCount: number, namedConsumerStrategy: string)
@ -35,13 +39,65 @@ export class GossipSeed {
readonly hostHeader: string; readonly hostHeader: string;
} }
export interface ProjectionDetails {
readonly coreProcessingTime: number,
readonly version: number,
readonly epoch: number,
readonly effectiveName: string,
readonly writesInProgress: number,
readonly readsInProgress: number,
readonly partitionsCached: number,
readonly status: string,
readonly stateReason: string,
readonly name: string,
readonly mode: string,
readonly position: string,
readonly progress: number,
readonly lastCheckpoint: string,
readonly eventsProcessedAfterRestart: number,
readonly statusUrl: string,
readonly stateUrl: string,
readonly resultUrl: string,
readonly queryUrl: string,
readonly enableCommandUrl: string,
readonly disableCommandUrl: string,
readonly checkpointStatus: string,
readonly bufferedEvents: number,
readonly writePendingEventsBeforeCheckpoint: number,
readonly writePendingEventsAfterCheckpoint: number
}
export class ProjectionsManager {
constructor(log: Logger, httpEndPoint: string, operationTimeout: number);
enable(name: string, userCredentials: UserCredentials): Promise<void>;
disable(name: string, userCredentials: UserCredentials): Promise<void>;
abort(name: string, userCredentials: UserCredentials): Promise<void>;
reset(name: string, userCredentials: UserCredentials): Promise<void>;
createOneTime(query: string, userCredentials: UserCredentials): Promise<void>;
createTransient(name: string, query: string, userCredentials: UserCredentials): Promise<void>;
createContinuous(name: string, query: string, trackEmittedStreams: boolean, userCredentials: UserCredentials): Promise<void>;
listAll(userCredentials: UserCredentials): Promise<ProjectionDetails[]>;
listOneTime(userCredentials: UserCredentials): Promise<ProjectionDetails[]>;
listContinuous(userCredentials: UserCredentials): Promise<ProjectionDetails[]>;
getStatus(name: string, userCredentials: UserCredentials): Promise<string>;
getState(name: string, userCredentials: UserCredentials): Promise<string>;
getPartitionState(name: string, partitionId: string, userCredentials: UserCredentials): Promise<string>;
getResult(name: string, userCredentials: UserCredentials): Promise<string>;
getPartitionResult(name: string, partitionId: string, userCredentials: UserCredentials): Promise<string>;
getStatistics(name: string, userCredentials: UserCredentials): Promise<string>;
getQuery(name: string, userCredentials: UserCredentials): Promise<string>;
getState(name: string, userCredentials: UserCredentials): Promise<string>;
updateQuery(name: string, query: string, userCredentials: UserCredentials): Promise<void>;
delete(name: string, deleteEmittedStreams: boolean, deleteStateStream: boolean, deleteCheckpointStream: boolean, userCredentials: UserCredentials): Promise<void>;
}
// Expose errors // Expose errors
export class WrongExpectedVersionError { export class WrongExpectedVersionError {
readonly name: string; readonly name: string;
readonly action: string; readonly action: string;
readonly message: string; readonly message: string;
readonly stream?: string; readonly stream?: string;
readonly expectedVersion?: number; readonly expectedVersion?: Long;
readonly transactionId?: Long; readonly transactionId?: Long;
} }
@ -59,12 +115,17 @@ export class AccessDeniedError {
readonly transactionId?: Long; readonly transactionId?: Long;
} }
// Expose enums/constants export class ProjectionCommandFailedError {
readonly httpStatusCode: number;
readonly message: string;
}
// Expose enums/constants
export namespace expectedVersion { export namespace expectedVersion {
const any: number; const any: number;
const noStream: number; const noStream: number;
const emptyStream: number; const emptyStream: number;
const streamExists: number;
} }
export namespace positions { export namespace positions {
@ -72,12 +133,17 @@ export namespace positions {
const end: Position; const end: Position;
} }
export namespace streamPosition {
const start: number;
const end: number;
}
//TODO
// systemMetadata // systemMetadata
// eventReadStatus // eventReadStatus
// sliceReadStatus // sliceReadStatus
// Expose loggers // Expose loggers
export interface Logger { export interface Logger {
debug(fmt: string, ...args: any[]): void; debug(fmt: string, ...args: any[]): void;
info(fmt: string, ...args: any[]): void; info(fmt: string, ...args: any[]): void;
@ -98,18 +164,18 @@ export class FileLogger implements Logger {
error(fmt: string, ...args: any[]): void; error(fmt: string, ...args: any[]): void;
} }
// // Expose results
export interface WriteResult { export interface WriteResult {
readonly nextExpectedVersion: number; readonly nextExpectedVersion: Long;
readonly logPosition: Position; readonly logPosition: Position;
} }
export interface RecordedEvent { export interface RecordedEvent {
readonly eventStreamId: string; readonly eventStreamId: string;
readonly eventId: string; readonly eventId: string;
readonly eventNumber: number; readonly eventNumber: Long;
readonly eventType: string; readonly eventType: string;
readonly created: Date;
readonly createdEpoch: number; readonly createdEpoch: number;
readonly data?: Buffer; readonly data?: Buffer;
readonly metadata?: Buffer; readonly metadata?: Buffer;
@ -123,17 +189,17 @@ export interface ResolvedEvent {
readonly isResolved: boolean; readonly isResolved: boolean;
readonly originalPosition?: Position; readonly originalPosition?: Position;
readonly originalStreamId: string; readonly originalStreamId: string;
readonly originalEventNumber: number; readonly originalEventNumber: Long;
} }
export interface StreamEventsSlice { export interface StreamEventsSlice {
readonly status: string; // TODO: enum readonly status: string; // TODO: enum
readonly stream: string; readonly stream: string;
readonly fromEventNumber: number; readonly fromEventNumber: Long;
readonly readDirection: string; // TODO: enum readonly readDirection: string; // TODO: enum
readonly events: ResolvedEvent[]; readonly events: ResolvedEvent[];
readonly nextEventNumber: number; readonly nextEventNumber: Long;
readonly lastEventNumber: number; readonly lastEventNumber: Long;
readonly isEndOfStream: boolean; readonly isEndOfStream: boolean;
} }
@ -159,7 +225,7 @@ export interface EventStoreTransaction {
export interface EventReadResult { export interface EventReadResult {
readonly status: string; readonly status: string;
readonly stream: string; readonly stream: string;
readonly eventNumber: number; readonly eventNumber: Long;
readonly event: ResolvedEvent | null; readonly event: ResolvedEvent | null;
} }
@ -167,7 +233,7 @@ export interface EventStoreSubscription {
readonly isSubscribedToAll: boolean; readonly isSubscribedToAll: boolean;
readonly streamId: string; readonly streamId: string;
readonly lastCommitPosition: Position; readonly lastCommitPosition: Position;
readonly lastEventNumber: number; readonly lastEventNumber: Long;
close(): void; close(): void;
unsubscribe(): void; unsubscribe(): void;
@ -194,7 +260,7 @@ export interface EventStorePersistentSubscription {
export interface RawStreamMetadataResult { export interface RawStreamMetadataResult {
readonly stream: string; readonly stream: string;
readonly isStreamDeleted: boolean; readonly isStreamDeleted: boolean;
readonly metastreamVersion: number; readonly metastreamVersion: Long;
readonly streamMetadata: any; readonly streamMetadata: any;
} }
@ -229,12 +295,12 @@ export interface TcpEndPoint {
} }
export interface HeartbeatInfo { export interface HeartbeatInfo {
connectionId: string; readonly connectionId: string;
remoteEndPoint: TcpEndPoint; readonly remoteEndPoint: TcpEndPoint;
requestSentAt: number; readonly requestSentAt: number;
requestPkgNumber: number; readonly requestPkgNumber: number;
responseReceivedAt: number; readonly responseReceivedAt: number;
responsePkgNumber: number; readonly responsePkgNumber: number;
} }
export interface EventData { export interface EventData {
@ -245,40 +311,47 @@ export interface EventData {
readonly metadata: Buffer; readonly metadata: Buffer;
} }
export interface EventStoreNodeConnection { interface EventStoreNodeConnectionEvents {
connected: TcpEndPoint;
disconnected: TcpEndPoint;
reconnecting: void;
closed:string;
error: Error;
heartbeatInfo: HeartbeatInfo;
}
type EventStoreNodeConnectionEventEmitter = StrictEventEmitter<EventEmitter, EventStoreNodeConnectionEvents>;
export class EventStoreNodeConnection extends (EventEmitter as { new(): EventStoreNodeConnectionEventEmitter }) {
connect(): Promise<void>; connect(): Promise<void>;
close(): void; close(): void;
// write actions // write actions
deleteStream(stream: string, expectedVersion: number, hardDelete?: boolean, userCredentials?: UserCredentials): Promise<DeleteResult>; deleteStream(stream: string, expectedVersion: Long|number, hardDelete?: boolean, userCredentials?: UserCredentials): Promise<DeleteResult>;
appendToStream(stream: string, expectedVersion: number, eventOrEvents: EventData | EventData[], userCredentials?: UserCredentials): Promise<WriteResult>; appendToStream(stream: string, expectedVersion: Long|number, eventOrEvents: EventData | EventData[], userCredentials?: UserCredentials): Promise<WriteResult>;
startTransaction(stream: string, expectedVersion: number, userCredentials?: UserCredentials): Promise<EventStoreTransaction>; startTransaction(stream: string, expectedVersion: Long|number, userCredentials?: UserCredentials): Promise<EventStoreTransaction>;
continueTransaction(transactionId: number, userCredentials?: UserCredentials): EventStoreTransaction; continueTransaction(transactionId: number, userCredentials?: UserCredentials): EventStoreTransaction;
// read actions // read actions
readEvent(stream: string, eventNumber: number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<EventReadResult>; readEvent(stream: string, eventNumber: Long|number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<EventReadResult>;
readStreamEventsForward(stream: string, start: number, count: number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<StreamEventsSlice>; readStreamEventsForward(stream: string, start: Long|number, count: number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<StreamEventsSlice>;
readStreamEventsBackward(stream: string, start: number, count: number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<StreamEventsSlice>; readStreamEventsBackward(stream: string, start: Long|number, count: number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<StreamEventsSlice>;
readAllEventsForward(position: Position, maxCount: number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<AllEventsSlice>; readAllEventsForward(position: Position, maxCount: number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<AllEventsSlice>;
readAllEventsBackward(position: Position, maxCount: number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<AllEventsSlice>; readAllEventsBackward(position: Position, maxCount: number, resolveLinkTos?: boolean, userCredentials?: UserCredentials): Promise<AllEventsSlice>;
// subscription actions // subscription actions
subscribeToStream(stream: string, resolveLinkTos: boolean, eventAppeared: EventAppearedCallback<EventStoreSubscription>, subscriptionDropped?: SubscriptionDroppedCallback<EventStoreSubscription>, userCredentials?: UserCredentials): Promise<EventStoreSubscription>; 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; subscribeToStreamFrom(stream: string, lastCheckpoint: Long|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>; 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; subscribeToAllFrom(lastCheckpoint: Position|null, resolveLinkTos: boolean, eventAppeared: EventAppearedCallback<EventStoreCatchUpSubscription>, liveProcessingStarted?: LiveProcessingStartedCallback, subscriptionDropped?: SubscriptionDroppedCallback<EventStoreCatchUpSubscription>, userCredentials?: UserCredentials, readBatchSize?: number): EventStoreCatchUpSubscription;
// persistent subscriptions // persistent subscriptions
createPersistentSubscription(stream: string, groupName: string, settings: PersistentSubscriptionSettings, userCredentials?: PersistentSubscriptionSettings): Promise<PersistentSubscriptionCreateResult>; createPersistentSubscription(stream: string, groupName: string, settings: PersistentSubscriptionSettings, userCredentials?: UserCredentials): Promise<PersistentSubscriptionCreateResult>;
updatePersistentSubscription(stream: string, groupName: string, settings: PersistentSubscriptionSettings, userCredentials?: PersistentSubscriptionSettings): Promise<PersistentSubscriptionUpdateResult>; updatePersistentSubscription(stream: string, groupName: string, settings: PersistentSubscriptionSettings, userCredentials?: UserCredentials): Promise<PersistentSubscriptionUpdateResult>;
deletePersistentSubscription(stream: string, groupName: string, userCredentials?: PersistentSubscriptionSettings): Promise<PersistentSubscriptionDeleteResult> deletePersistentSubscription(stream: string, groupName: string, userCredentials?: UserCredentials): Promise<PersistentSubscriptionDeleteResult>
connectToPersistentSubscription(stream: string, groupName: string, eventAppeared: EventAppearedCallback<EventStorePersistentSubscription>, subscriptionDropped?: SubscriptionDroppedCallback<EventStorePersistentSubscription>, userCredentials?: UserCredentials, bufferSize?: number, autoAck?: boolean): Promise<EventStorePersistentSubscription>; connectToPersistentSubscription(stream: string, groupName: string, eventAppeared: EventAppearedCallback<EventStorePersistentSubscription>, subscriptionDropped?: SubscriptionDroppedCallback<EventStorePersistentSubscription>, userCredentials?: UserCredentials, bufferSize?: number, autoAck?: boolean): Promise<EventStorePersistentSubscription>;
// metadata actions // metadata actions
setStreamMetadataRaw(stream: string, expectedMetastreamVersion: number, metadata: any, userCredentials?: UserCredentials): Promise<WriteResult>; setStreamMetadataRaw(stream: string, expectedMetastreamVersion: Long|number, metadata: any, userCredentials?: UserCredentials): Promise<WriteResult>;
getStreamMetadataRaw(stream: string, userCredentials?: UserCredentials): Promise<RawStreamMetadataResult>; getStreamMetadataRaw(stream: string, userCredentials?: UserCredentials): Promise<RawStreamMetadataResult>;
on(event: "connected" | "disconnected" | "reconnecting" | "closed" | "error" | "heartbeatInfo", listener: (arg: Error | string | TcpEndPoint | HeartbeatInfo) => void): this;
once(event: "connected" | "disconnected" | "reconnecting" | "closed" | "error" | "heartbeatInfo", listener: (arg: Error | string | TcpEndPoint | HeartbeatInfo) => void): this;
} }
// Expose helper functions // Expose helper functions
export interface ConnectionSettings { export interface ConnectionSettings {
log?: Logger, log?: Logger,
verboseLogging?: boolean, verboseLogging?: boolean,
@ -307,62 +380,12 @@ export interface ConnectionSettings {
// Cluster Settings // Cluster Settings
clusterDns?: string, clusterDns?: string,
maxDiscoverAttempts?: number, maxDiscoverAttempts?: number,
discoverDelay?: number,
externalGossipPort?: number, externalGossipPort?: number,
gossipTimeout?: number gossipTimeout?: number
} }
// Expose Helper functions
export function createConnection(settings: ConnectionSettings, endPointOrGossipSeed: string | TcpEndPoint | GossipSeed[], connectionName?: string): EventStoreNodeConnection; 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 createJsonEventData(eventId: string, event: any, metadata?: any, type?: string): EventData;
export function createEventData(eventId: string, type: string, isJson: boolean, data: Buffer, metadata?: Buffer): EventData; export function createEventData(eventId: string, type: string, isJson: boolean, data: Buffer, metadata?: Buffer): EventData;
// Projections
export interface ProjectionDetails {
coreProcessingTime: number,
version: number,
epoch: number,
effectiveName: string,
writesInProgress: number,
readsInProgress: number,
partitionsCached: number,
status: string,
stateReason: string,
name: string,
mode: string,
position: string,
progress: number,
lastCheckpoint: string,
eventsProcessedAfterRestart: number,
statusUrl: string,
stateUrl: string,
resultUrl: string,
queryUrl: string,
enableCommandUrl: string,
disableCommandUrl: string,
checkpointStatus: string,
bufferedEvents: number,
writePendingEventsBeforeCheckpoint: number,
writePendingEventsAfterCheckpoint: number
}
export class ProjectionsManager {
constructor(log: Logger, httpEndPoint: string, operationTimeout: number);
enable(name: string, userCredentials: UserCredentials): Promise<void>;
disable(name: string, userCredentials: UserCredentials): Promise<void>;
abort(name: string, userCredentials: UserCredentials): Promise<void>;
createOneTime(query: string, userCredentials: UserCredentials): Promise<void>;
createTransient(name: string, query: string, userCredentials: UserCredentials): Promise<void>;
createContinuous(name: string, query: string, trackEmittedStreams: boolean, userCredentials: UserCredentials): Promise<void>;
listAll(userCredentials: UserCredentials): Promise<ProjectionDetails[]>;
listOneTime(userCredentials: UserCredentials): Promise<ProjectionDetails[]>;
listContinuous(userCredentials: UserCredentials): Promise<ProjectionDetails[]>;
getStatus(name: string, userCredentials: UserCredentials): Promise<string>;
getState(name: string, userCredentials: UserCredentials): Promise<string>;
getPartitionState(name: string, partitionId: string, userCredentials: UserCredentials): Promise<string>;
getResult(name: string, userCredentials: UserCredentials): Promise<string>;
getPartitionResult(name: string, partitionId: string, userCredentials: UserCredentials): Promise<string>;
getStatistics(name: string, userCredentials: UserCredentials): Promise<string>;
getQuery(name: string, userCredentials: UserCredentials): Promise<string>;
getState(name: string, userCredentials: UserCredentials): Promise<string>;
updateQuery(name: string, query: string, userCredentials: UserCredentials): Promise<void>;
deleteQuery(name: string, deleteEmittedStreams: boolean, userCredentials: UserCredentials): Promise<void>;
}

6891
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,34 @@
{ {
"name": "node-eventstore-client", "name": "node-eventstore-client",
"version": "0.1.11", "version": "22.10.0",
"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", "types": "index.d.ts",
"engines": { "engines": {
"node": ">=4.0" "node": ">=14",
"npm": ">=6"
}, },
"engineStrict": true, "engineStrict": true,
"scripts": { "scripts": {
"clean": "rm lib/dist.js", "clean": "rm -f lib/dist.js",
"prebuild": "npm run clean",
"build": "webpack", "build": "webpack",
"pretest": "npm run build", "pretest": "npm run build",
"test": "nodeunit", "test": "nodeunit",
"test-debug": "TESTS_VERBOSE_LOGGING=1 nodeunit", "test-debug": "TESTS_VERBOSE_LOGGING=1 nodeunit",
"prepublish": "npm run build && npm run gendocs", "test:jest:watch": "jest --watch --coverage",
"test:single": "npm run compose:single:start && npm run compose:wait && npm run compose:single:test ; npm run compose:single:stop",
"test:gossip": "npm run compose:cluster:start && npm run compose:wait && npm run compose:gossip:test ; npm run compose:cluster:stop",
"test:cluster": "npm run compose:cluster:start && npm run compose:wait && npm run compose:cluster:test ; npm run compose:cluster:stop",
"compose:single:start": "docker-compose -f docker-compose-single.yaml up --build -d",
"compose:cluster:start": "docker-compose -f docker-compose-cluster.yaml up --build -d",
"compose:single:stop": "docker-compose -f docker-compose-single.yaml down -v --remove-orphans",
"compose:cluster:stop": "docker-compose -f docker-compose-cluster.yaml down -v --remove-orphans",
"compose:wait": "while [ \"$(curl -s -o /dev/null -w ''%{http_code}'' -k https://localhost:2113/ping)\" != \"200\" ]; do sleep 5; done",
"compose:single:test": "docker-compose -f docker-compose-single.yaml exec nodejs bash -c \"EVENTSTORE_CONNECTION_TYPE=tcp EVENTSTORE_HOST=192.168.33.10 ./build-and-test.sh\"",
"compose:gossip:test": "docker-compose -f docker-compose-cluster.yaml exec nodejs bash -c \"EVENTSTORE_CONNECTION_TYPE=gossip EVENTSTORE_HOST=192.168.33.10 EVENTSTORE_HOST_1=192.168.33.10 EVENTSTORE_HOST_2=192.168.33.11 EVENTSTORE_HOST_3=192.168.33.12 ./build-and-test.sh\"",
"compose:cluster:test": "docker-compose -f docker-compose-cluster.yaml exec nodejs bash -c \"EVENTSTORE_CONNECTION_TYPE=dns EVENTSTORE_HOST=cluster.tests.node-eventstore-client.local ./build-and-test.sh\"",
"prepublishOnly": "npm run build && npm run gendocs",
"gendocs": "rm -rf docs && jsdoc src -r -d docs" "gendocs": "rm -rf docs && jsdoc src -r -d docs"
}, },
"files": [ "files": [
@ -45,15 +59,18 @@
}, },
"homepage": "https://github.com/nicdex/node-eventstore-client#readme", "homepage": "https://github.com/nicdex/node-eventstore-client#readme",
"dependencies": { "dependencies": {
"@types/long": "^3.0.31", "@types/node": ">=14",
"@types/node": "^6.0.47", "long": "^5.2.0",
"long": "^3.2", "strict-event-emitter-types": "^1.2.0",
"protobufjs": "^6.7.3", "uuid": "^9.0.0"
"uuid": "^3.0.1"
}, },
"devDependencies": { "devDependencies": {
"jsdoc": "^3.5.3", "jest": "^29.4.1",
"nodeunit": "^0.11.1", "jsdoc": "^4.0.0",
"webpack": "^3.3.0" "nodeunit": "^0.11.3",
"protobufjs": "^7.2.0",
"protobufjs-cli": "^1.1.0",
"webpack": "^5.75.0",
"webpack-cli": "^3.3.10"
} }
} }

62
samples/issue-60.js Normal file
View File

@ -0,0 +1,62 @@
const client = require('../src/client'); // RWM: Import from npm installed package rather than from src
//const client = require("node-eventstore-client");
const resolveLinkTos = true;
function resumeEvent(event) {
return [
event.originalEvent.eventType,
[event.originalEventNumber.toNumber(), event.originalStreamId].join('@'),
event.originalPosition
].join(" ")
}
const eventAppeared = (subscription, event) => console.log("Event received", resumeEvent(event));
const subscriptionDropped = (subscription, reason, error) => console.log("Subscription dropped", reason, error);
const libeProcessingStarted = () => console.log("Live processing started.");
const credentials = new client.UserCredentials("admin", "changeit");
const settings = {
maxReconnections: 10,
reconnectionDelay: 1000, // RWM: slow down the reconnection attempts. 10 seconds to restore connection.
};
if (process.env.ENABLE_LOGGING) settings.log = console;
if (process.env.VERBOSE) settings.verboseLogging = true;
const endpoint = "tcp://localhost:1113";
const connection = client.createConnection(settings, endpoint);
connection.connect().catch(err => console.log("Connection failed", err));
connection.on('heartbeatInfo', heartbeatInfo =>
console.log('Heartbeat latency', heartbeatInfo.responseReceivedAt - heartbeatInfo.requestSentAt, 'ms')
);
connection.once("connected", tcpEndPoint => {
console.log(`Connected to eventstore at ${tcpEndPoint.host}:${tcpEndPoint.port}`);
// RWM: subscribe Stream instead of All
connection.subscribeToStreamFrom(
"test", // RWM: Stream to subscribe to
null,
resolveLinkTos,
eventAppeared,
libeProcessingStarted,
subscriptionDropped,
credentials
);
});
connection.on("error", error =>
console.log(`Error occurred on connection: ${error}`)
)
connection.on("closed", reason =>
console.log(`Connection closed, reason: ${reason}`)
)
// RWM: Handle the reconnecting event, for better awareness of what's happening
connection.on("reconnecting", msg =>
console.log(`Reconnecting, msg: ${JSON.stringify(msg, null, 4)}`)
)

View File

@ -0,0 +1,50 @@
const client = require('../src/client');
//const client = require("node-eventstore-client");
const resolveLinkTos = true;
function resumeEvent(event) {
return [
event.originalEvent.eventType,
[event.originalEventNumber.toNumber(), event.originalStreamId].join('@'),
event.originalPosition
].join(" ")
}
const eventAppeared = (subscription, event) => console.log("Event received", resumeEvent(event));
const subscriptionDropped = (subscription, reason, error) => console.log("Subscription dropped", reason, error);
const libeProcessingStarted = () => console.log("Live processing started.");
const credentials = new client.UserCredentials("admin", "changeit");
const settings = {};
const endpoint = "tcp://localhost:1113";
const connection = client.createConnection(settings, endpoint);
connection.connect().catch(err => console.log("Connection failed", err));
connection.on('heartbeatInfo', heartbeatInfo =>
console.log('Heartbeat latency', heartbeatInfo.responseReceivedAt - heartbeatInfo.requestSentAt, 'ms')
);
connection.once("connected", tcpEndPoint => {
console.log(`Connected to eventstore at ${tcpEndPoint.host}:${tcpEndPoint.port}`);
connection.subscribeToAllFrom(
null,
resolveLinkTos,
eventAppeared,
libeProcessingStarted,
subscriptionDropped,
credentials
);
});
connection.on("error", error =>
console.log(`Error occurred on connection: ${error}`)
)
connection.on("closed", reason =>
console.log(`Connection closed, reason: ${reason}`)
)

View File

@ -4,15 +4,23 @@ var results = require('./results');
const expectedVersion = { const expectedVersion = {
any: -2, any: -2,
noStream: -1, noStream: -1,
emptyStream: -1 emptyStream: -1,
streamExists: -4
}; };
Object.freeze(expectedVersion); Object.freeze(expectedVersion);
const positions = { const positions = {
start: new results.Position(0, 0), start: new results.Position(0, 0),
end: new results.Position(-1, -1) end: new results.Position(-1, -1)
}; };
Object.freeze(positions); Object.freeze(positions);
const streamPosition = {
start: 0,
end: -1
};
Object.freeze(streamPosition);
/** /**
* Create an EventData object from JavaScript event/metadata that will be serialized as json * Create an EventData object from JavaScript event/metadata that will be serialized as json
* @public * @public
@ -25,8 +33,8 @@ Object.freeze(positions);
function createJsonEventData(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 = Buffer.from(JSON.stringify(event));
var metaBuf = metadata ? new Buffer(JSON.stringify(metadata)) : null; var metaBuf = metadata ? Buffer.from(JSON.stringify(metadata)) : null;
return new EventData(eventId, type || event.constructor.name, true, eventBuf, metaBuf); return new EventData(eventId, type || event.constructor.name, true, eventBuf, metaBuf);
} }
@ -45,28 +53,31 @@ function createEventData(eventId, type, isJson, data, metadata) {
} }
// Expose classes // Expose classes
module.exports.Position = results.Position; exports.Position = results.Position;
module.exports.UserCredentials = require('./systemData/userCredentials'); exports.UserCredentials = require('./systemData/userCredentials');
module.exports.PersistentSubscriptionSettings = require('./persistentSubscriptionSettings'); exports.PersistentSubscriptionSettings = require('./persistentSubscriptionSettings');
module.exports.SystemConsumerStrategies = require('./systemConsumerStrategies'); exports.SystemConsumerStrategies = require('./systemConsumerStrategies');
module.exports.GossipSeed = require('./gossipSeed'); exports.GossipSeed = require('./gossipSeed');
module.exports.EventStoreConnection = require('./eventStoreConnection'); exports.EventStoreConnection = require('./eventStoreConnection');
module.exports.ProjectionsManager = require('./projections/projectionsManager'); exports.ProjectionsManager = require('./projections/projectionsManager');
// Expose errors // Expose errors
module.exports.WrongExpectedVersionError = require('./errors/wrongExpectedVersionError'); exports.WrongExpectedVersionError = require('./errors/wrongExpectedVersionError');
module.exports.StreamDeletedError = require('./errors/streamDeletedError'); exports.StreamDeletedError = require('./errors/streamDeletedError');
module.exports.AccessDeniedError = require('./errors/accessDeniedError'); exports.AccessDeniedError = require('./errors/accessDeniedError');
module.exports.ProjectionCommandFailedError = require('./errors/projectionCommandFailedError'); exports.ProjectionCommandFailedError = require('./errors/projectionCommandFailedError');
// Expose enums/constants // Expose enums/constants
module.exports.expectedVersion = expectedVersion; exports.expectedVersion = expectedVersion;
module.exports.positions = positions; exports.positions = positions;
module.exports.systemMetadata = require('./common/systemMetadata'); exports.streamPosition = streamPosition;
module.exports.eventReadStatus = results.EventReadStatus; exports.systemMetadata = require('./common/systemMetadata');
module.exports.sliceReadStatus = require('./sliceReadStatus'); exports.eventReadStatus = results.EventReadStatus;
exports.sliceReadStatus = require('./sliceReadStatus');
exports.persistentSubscriptionNakEventAction =
exports.PersistentSubscriptionNakEventAction = require('./persistentSubscriptionNakEventAction');
// Expose loggers // Expose loggers
module.exports.NoopLogger = require('./common/log/noopLogger'); exports.NoopLogger = require('./common/log/noopLogger');
module.exports.FileLogger = require('./common/log/fileLogger'); exports.FileLogger = require('./common/log/fileLogger');
// Expose Helper functions // Expose Helper functions
module.exports.createConnection = require('./eventStoreConnection').create; exports.createConnection = require('./eventStoreConnection').create;
module.exports.createJsonEventData = createJsonEventData; exports.createJsonEventData = createJsonEventData;
module.exports.createEventData = createEventData; exports.createEventData = createEventData;

View File

@ -37,15 +37,14 @@ AppendToStreamOperation.prototype._createRequestDto = function() {
eventStreamId: this._stream, eventStreamId: this._stream,
expectedVersion: this._expectedVersion, expectedVersion: this._expectedVersion,
events: dtos, events: dtos,
requireMaster: this._requireMaster}); requireLeader: this._requireMaster});
}; };
AppendToStreamOperation.prototype._inspectResponse = function(response) { AppendToStreamOperation.prototype._inspectResponse = function(response) {
switch (response.result) switch (response.result)
{ {
case ClientMessage.OperationResult.Success: case ClientMessage.OperationResult.Success:
if (this._wasCommitTimeout) if (this._wasCommitTimeout) this.log.debug("IDEMPOTENT WRITE SUCCEEDED FOR %s.", this);
this.log.debug("IDEMPOTENT WRITE SUCCEEDED FOR %s.", this);
this._succeed(); this._succeed();
return new InspectionResult(InspectionDecision.EndOperation, "Success"); return new InspectionResult(InspectionDecision.EndOperation, "Success");
case ClientMessage.OperationResult.PrepareTimeout: case ClientMessage.OperationResult.PrepareTimeout:

View File

@ -1,5 +1,4 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand'); var TcpCommand = require('../systemData/tcpCommand');
var InspectionDecision = require('../systemData/inspectionDecision'); var InspectionDecision = require('../systemData/inspectionDecision');
@ -25,7 +24,7 @@ util.inherits(CommitTransactionOperation, OperationBase);
CommitTransactionOperation.prototype._createRequestDto = function() { CommitTransactionOperation.prototype._createRequestDto = function() {
return new ClientMessage.TransactionCommit({ return new ClientMessage.TransactionCommit({
transactionId: this._transactionId, transactionId: this._transactionId,
requireMaster: this._requireMaster requireLeader: this._requireMaster
}); });
}; };

View File

@ -1,5 +1,4 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid');
var ensure = require('../common/utils/ensure'); var ensure = require('../common/utils/ensure');
var OperationBase = require('../clientOperations/operationBase'); var OperationBase = require('../clientOperations/operationBase');

View File

@ -1,7 +1,5 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid');
var ensure = require('../common/utils/ensure');
var OperationBase = require('../clientOperations/operationBase'); var OperationBase = require('../clientOperations/operationBase');
var TcpCommand = require('../systemData/tcpCommand'); var TcpCommand = require('../systemData/tcpCommand');
var ClientMessage = require('../messages/clientMessage'); var ClientMessage = require('../messages/clientMessage');

View File

@ -1,5 +1,4 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand'); var TcpCommand = require('../systemData/tcpCommand');
var InspectionDecision = require('../systemData/inspectionDecision'); var InspectionDecision = require('../systemData/inspectionDecision');
@ -28,7 +27,7 @@ DeleteStreamOperation.prototype._createRequestDto = function() {
return new ClientMessage.DeleteStream({ return new ClientMessage.DeleteStream({
eventStreamId: this._stream, eventStreamId: this._stream,
expectedVersion: this._expectedVersion, expectedVersion: this._expectedVersion,
requireMaster: this._requireMaster, requireLeader: this._requireMaster,
hardDelete: this._hardDelete hardDelete: this._hardDelete
}); });
}; };

View File

@ -42,10 +42,11 @@ OperationBase.prototype._succeed = function() {
if (!this._completed) { if (!this._completed) {
this._completed = true; this._completed = true;
if (this._response) 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."))
}
} }
}; };
@ -116,9 +117,9 @@ OperationBase.prototype._inspectNotHandled = function(pkg)
case ClientMessage.NotHandled.NotHandledReason.TooBusy: case ClientMessage.NotHandled.NotHandledReason.TooBusy:
return new InspectionResult(InspectionDecision.Retry, "NotHandled - TooBusy"); return new InspectionResult(InspectionDecision.Retry, "NotHandled - TooBusy");
case ClientMessage.NotHandled.NotHandledReason.NotMaster: case ClientMessage.NotHandled.NotHandledReason.NotLeader:
var masterInfo = ClientMessage.NotHandled.MasterInfo.decode(message.additionalInfo); var masterInfo = ClientMessage.NotHandled.LeaderInfo.decode(message.additionalInfo);
return new InspectionResult(InspectionDecision.Reconnect, "NotHandled - NotMaster", return new InspectionResult(InspectionDecision.Reconnect, "NotHandled - NotLeader",
{host: masterInfo.externalTcpAddress, port: masterInfo.externalTcpPort}, {host: masterInfo.externalTcpAddress, port: masterInfo.externalTcpPort},
{host: masterInfo.externalSecureTcpAddress, port: masterInfo.externalSecureTcpPort}); {host: masterInfo.externalSecureTcpAddress, port: masterInfo.externalSecureTcpPort});
@ -130,8 +131,7 @@ 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"

View File

@ -1,5 +1,4 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand'); var TcpCommand = require('../systemData/tcpCommand');
var ClientMessage = require('../messages/clientMessage'); var ClientMessage = require('../messages/clientMessage');
@ -30,7 +29,7 @@ ReadAllEventsBackwardOperation.prototype._createRequestDto = function() {
preparePosition: this._position.preparePosition, preparePosition: this._position.preparePosition,
maxCount: this._maxCount, maxCount: this._maxCount,
resolveLinkTos: this._resolveLinkTos, resolveLinkTos: this._resolveLinkTos,
requireMaster: this._requireMaster requireLeader: this._requireMaster
}); });
}; };

View File

@ -1,5 +1,4 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand'); var TcpCommand = require('../systemData/tcpCommand');
var ClientMessage = require('../messages/clientMessage'); var ClientMessage = require('../messages/clientMessage');
@ -30,7 +29,7 @@ ReadAllEventsForwardOperation.prototype._createRequestDto = function() {
preparePosition: this._position.preparePosition, preparePosition: this._position.preparePosition,
maxCount: this._maxCount, maxCount: this._maxCount,
resolveLinkTos: this._resolveLinkTos, resolveLinkTos: this._resolveLinkTos,
requireMaster: this._requireMaster requireLeader: this._requireMaster
}); });
}; };

View File

@ -25,7 +25,7 @@ ReadEventOperation.prototype._createRequestDto = function() {
eventStreamId: this._stream, eventStreamId: this._stream,
eventNumber: this._eventNumber, eventNumber: this._eventNumber,
resolveLinkTos: this._resolveLinkTos, resolveLinkTos: this._resolveLinkTos,
requireMaster: this._requireMaster requireLeader: this._requireMaster
}); });
}; };
@ -59,7 +59,6 @@ ReadEventOperation.prototype._transformResponse = function(response) {
return new results.EventReadResult(convert(response.result), this._stream, this._eventNumber, response.event); return new results.EventReadResult(convert(response.result), this._stream, this._eventNumber, response.event);
}; };
function convert(result) function convert(result)
{ {
switch (result) switch (result)

View File

@ -1,5 +1,4 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand'); var TcpCommand = require('../systemData/tcpCommand');
var ClientMessage = require('../messages/clientMessage'); var ClientMessage = require('../messages/clientMessage');
@ -32,7 +31,7 @@ ReadStreamEventsBackwardOperation.prototype._createRequestDto = function() {
fromEventNumber: this._fromEventNumber, fromEventNumber: this._fromEventNumber,
maxCount: this._maxCount, maxCount: this._maxCount,
resolveLinkTos: this._resolveLinkTos, resolveLinkTos: this._resolveLinkTos,
requireMaster: this._requireMaster requireLeader: this._requireMaster
}); });
}; };

View File

@ -1,5 +1,4 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand'); var TcpCommand = require('../systemData/tcpCommand');
var ClientMessage = require('../messages/clientMessage'); var ClientMessage = require('../messages/clientMessage');
@ -32,7 +31,7 @@ ReadStreamEventsForwardOperation.prototype._createRequestDto = function() {
fromEventNumber: this._fromEventNumber, fromEventNumber: this._fromEventNumber,
maxCount: this._maxCount, maxCount: this._maxCount,
resolveLinkTos: this._resolveLinkTos, resolveLinkTos: this._resolveLinkTos,
requireMaster: this._requireMaster requireLeader: this._requireMaster
}); });
}; };

View File

@ -1,12 +1,10 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand'); 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 EventStoreTransaction = require('../eventStoreTransaction'); var EventStoreTransaction = require('../eventStoreTransaction');
var results = require('../results');
var AccessDeniedError = require('../errors/accessDeniedError'); var AccessDeniedError = require('../errors/accessDeniedError');
var WrongExpectedVersionError = require('../errors/wrongExpectedVersionError'); var WrongExpectedVersionError = require('../errors/wrongExpectedVersionError');
var StreamDeletedError = require('../errors/streamDeletedError'); var StreamDeletedError = require('../errors/streamDeletedError');
@ -27,7 +25,7 @@ StartTransactionOperation.prototype._createRequestDto = function() {
return new ClientMessage.TransactionStart({ return new ClientMessage.TransactionStart({
eventStreamId: this._stream, eventStreamId: this._stream,
expectedVersion: this._expectedVersion, expectedVersion: this._expectedVersion,
requireMaster: this._requireMaster requireLeader: this._requireMaster
}); });
}; };

View File

@ -1,5 +1,4 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid');
var TcpCommand = require('../systemData/tcpCommand'); var TcpCommand = require('../systemData/tcpCommand');
var TcpFlags = require('../systemData/tcpFlags'); var TcpFlags = require('../systemData/tcpFlags');
@ -46,8 +45,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) if (this._subscription !== null || this._unsubscribed) return false;
return false;
this._correlationId = correlationId; this._correlationId = correlationId;
connection.enqueueSend(this._createSubscriptionPackage()); connection.enqueueSend(this._createSubscriptionPackage());
@ -76,9 +74,7 @@ SubscriptionOperation.prototype.inspectPackage = function(pkg) {
try try
{ {
var result = this._inspectPackage(pkg); var result = this._inspectPackage(pkg);
if (result !== null) { if (result !== null) return result;
return result;
}
switch (pkg.command) switch (pkg.command)
{ {
@ -128,8 +124,9 @@ 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());
switch (message.reason) switch (message.reason)
@ -140,9 +137,9 @@ SubscriptionOperation.prototype.inspectPackage = function(pkg) {
case ClientMessage.NotHandled.NotHandledReason.TooBusy: case ClientMessage.NotHandled.NotHandledReason.TooBusy:
return new InspectionResult(InspectionDecision.Retry, "NotHandled - TooBusy"); return new InspectionResult(InspectionDecision.Retry, "NotHandled - TooBusy");
case ClientMessage.NotHandled.NotHandledReason.NotMaster: case ClientMessage.NotHandled.NotHandledReason.NotLeader:
var masterInfo = ClientMessage.NotHandled.MasterInfo.decode(message.additionalInfo); var masterInfo = ClientMessage.NotHandled.LeaderInfo.decode(message.additionalInfo);
return new InspectionResult(InspectionDecision.Reconnect, "NotHandled - NotMaster", return new InspectionResult(InspectionDecision.Reconnect, "NotHandled - NotLeader",
{host: masterInfo.externalTcpAddress, port: masterInfo.externalTcpPort}, {host: masterInfo.externalTcpAddress, port: masterInfo.externalTcpPort},
{host: masterInfo.externalSecureTcpAddress, port: masterInfo.externalSecureTcpPort}); {host: masterInfo.externalSecureTcpAddress, port: masterInfo.externalSecureTcpPort});
@ -172,8 +169,7 @@ SubscriptionOperation.prototype.connectionClosed = function() {
}; };
SubscriptionOperation.prototype.timeOutSubscription = function() { SubscriptionOperation.prototype.timeOutSubscription = function() {
if (this._subscription !== null) if (this._subscription !== null) return false;
return false;
this.dropSubscription(SubscriptionDropReason.SubscribingError, null); this.dropSubscription(SubscriptionDropReason.SubscribingError, null);
return true; return true;
}; };
@ -182,9 +178,10 @@ SubscriptionOperation.prototype.dropSubscription = function(reason, err, connect
if (!this._unsubscribed) if (!this._unsubscribed)
{ {
this._unsubscribed = true; this._unsubscribed = true;
if (this._verboseLogging) if (this._verboseLogging) {
this._log.debug("Subscription %s to %s: closing subscription, reason: %s, exception: %s...", this._log.debug("Subscription %s to %s: closing subscription, reason: %s, exception: %s...",
this._correlationId, this._streamId || "<all>", reason, err); this._correlationId, this._streamId || "<all>", reason, err);
}
if (reason !== SubscriptionDropReason.UserInitiated && this._subscription === null) if (reason !== SubscriptionDropReason.UserInitiated && this._subscription === null)
{ {
@ -193,24 +190,31 @@ SubscriptionOperation.prototype.dropSubscription = function(reason, err, connect
return; return;
} }
if (reason === SubscriptionDropReason.UserInitiated && this._subscription !== null && connection !== null) if (reason === SubscriptionDropReason.UserInitiated && this._subscription !== null && connection !== null) {
connection.enqueueSend(this._createUnsubscriptionPackage()); connection.enqueueSend(this._createUnsubscriptionPackage());
}
var self = this; var self = this;
if (this._subscription !== null) if (this._subscription !== null) {
this._executeAction(function() { self._subscriptionDropped(self._subscription, reason, err); }); this._executeAction(function () {
self._subscriptionDropped(self._subscription, reason, err);
});
}
} }
}; };
SubscriptionOperation.prototype._confirmSubscription = function(lastCommitPosition, lastEventNumber) { SubscriptionOperation.prototype._confirmSubscription = function(lastCommitPosition, lastEventNumber) {
if (lastCommitPosition < -1) if (lastCommitPosition < -1) {
throw new Error(util.format("Invalid lastCommitPosition %s on subscription confirmation.", lastCommitPosition)); throw new Error(util.format("Invalid lastCommitPosition %s on subscription confirmation.", lastCommitPosition));
if (this._subscription !== null) }
if (this._subscription !== null) {
throw new Error("Double confirmation of subscription."); throw new Error("Double confirmation of subscription.");
}
if (this._verboseLogging) if (this._verboseLogging) {
this._log.debug("Subscription %s to %s: subscribed at CommitPosition: %d, EventNumber: %d.", this._log.debug("Subscription %s to %s: subscribed at CommitPosition: %d, EventNumber: %d.",
this._correlationId, this._streamId || "<all>", lastCommitPosition, lastEventNumber); this._correlationId, this._streamId || "<all>", lastCommitPosition, lastEventNumber);
}
this._subscription = this._createSubscriptionObject(lastCommitPosition, lastEventNumber); this._subscription = this._createSubscriptionObject(lastCommitPosition, lastEventNumber);
this._cb(null, this._subscription); this._cb(null, this._subscription);
@ -221,15 +225,15 @@ SubscriptionOperation.prototype._createSubscriptionObject = function(lastCommitP
}; };
SubscriptionOperation.prototype._onEventAppeared = function(e) { SubscriptionOperation.prototype._onEventAppeared = function(e) {
if (this._unsubscribed) if (this._unsubscribed) return;
return;
if (this._subscription === null) throw new Error("Subscription not confirmed, but event appeared!"); if (this._subscription === null) throw new Error("Subscription not confirmed, but event appeared!");
if (this._verboseLogging) if (this._verboseLogging) {
this._log.debug("Subscription %s to %s: event appeared (%s, %d, %s @ %s).", this._log.debug("Subscription %s to %s: event appeared (%s, %d, %s @ %s).",
this._correlationId, this._streamId || "<all>", this._correlationId, this._streamId || "<all>",
e.originalStreamId, e.originalEventNumber, e.originalEvent.eventType, e.originalPosition); e.originalStreamId, e.originalEventNumber, e.originalEvent.eventType, e.originalPosition);
}
var self = this; var self = this;
this._executeAction(function() { return self._eventAppeared(self._subscription, e); }); this._executeAction(function() { return self._eventAppeared(self._subscription, e); });

View File

@ -32,7 +32,7 @@ TransactionalWriteOperation.prototype._createRequestDto = function() {
return new ClientMessage.TransactionWrite({ return new ClientMessage.TransactionWrite({
transactionId: this._transactionId, transactionId: this._transactionId,
events: dtos, events: dtos,
requireMaster: this._requireMaster requireLeader: this._requireMaster
}); });
}; };
@ -65,4 +65,3 @@ TransactionalWriteOperation.prototype.toString = function() {
}; };
module.exports = TransactionalWriteOperation; module.exports = TransactionalWriteOperation;

View File

@ -1,5 +1,4 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid');
var ensure = require('../common/utils/ensure'); var ensure = require('../common/utils/ensure');
var OperationBase = require('../clientOperations/operationBase'); var OperationBase = require('../clientOperations/operationBase');

View File

@ -46,7 +46,7 @@ VolatileSubscriptionOperation.prototype._inspectPackage = function(pkg) {
} }
return null; return null;
} catch(e) { } catch(e) {
console.log(e.stack); this._log.warn(e.stack);
return null; return null;
} }
}; };

View File

@ -19,8 +19,7 @@ BufferSegment.prototype.toString = function() {
}; };
BufferSegment.prototype.toBuffer = function() { BufferSegment.prototype.toBuffer = function() {
if (this.offset === 0 && this.count === this.buffer.length) if (this.offset === 0 && this.count === this.buffer.length) return this.buffer;
return this.buffer;
return this.buffer.slice(this.offset, this.offset + this.count); return this.buffer.slice(this.offset, this.offset + this.count);
}; };

View File

@ -14,14 +14,14 @@ function parse(s, buf, offset) {
var ii = 0; var ii = 0;
if (buf) buf.fill(0, i, i + 16); if (buf) buf.fill(0, i, i + 16);
buf = buf || new Buffer(16); buf = buf || Buffer.alloc(16);
s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) { s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) {
if (ii < 16) { // Don't overflow! if (ii < 16) { // Don't overflow!
buf[i + ii++] = _hexToByte[oct]; buf[i + ii++] = _hexToByte[oct];
} }
}); });
var buf2 = new Buffer(buf.slice(i, i + 16)); var buf2 = Buffer.from(buf.slice(i, i + 16));
buf[i + 0] = buf2[3]; buf[i + 0] = buf2[3];
buf[i + 1] = buf2[2]; buf[i + 1] = buf2[2];
buf[i + 2] = buf2[1]; buf[i + 2] = buf2[1];

View File

@ -26,6 +26,11 @@ FileLogger.prototype.info = function() {
fs.appendFileSync(this._filePath, line); fs.appendFileSync(this._filePath, line);
}; };
FileLogger.prototype.warn = function() {
var line = createLine('WARN', arguments, 0);
fs.appendFileSync(this._filePath, line);
};
FileLogger.prototype.error = function(e) { FileLogger.prototype.error = function(e) {
var hasError = e instanceof Error; var hasError = e instanceof Error;
var line = createLine('ERROR', arguments, hasError ? 1 : 0); var line = createLine('ERROR', arguments, hasError ? 1 : 0);

View File

@ -1,7 +1,8 @@
function NoopLogger() { function NoopLogger() {
} }
NoopLogger.prototype.error = function() {};
NoopLogger.prototype.debug = function() {}; NoopLogger.prototype.debug = function() {};
NoopLogger.prototype.info = function() {}; NoopLogger.prototype.info = function() {};
NoopLogger.prototype.warn = function() {};
NoopLogger.prototype.error = function() {};
module.exports = NoopLogger; module.exports = NoopLogger;

View File

@ -1,5 +1,5 @@
const SystemEventTypes = { const SystemEventTypes = Object.freeze({
StreamMetadata: '$metadata' StreamMetadata: '$metadata'
}; });
module.exports = SystemEventTypes; module.exports = SystemEventTypes;

View File

@ -1,4 +1,4 @@
const SystemMetadata = { const SystemMetadata = Object.freeze({
maxAge: '$maxAge', maxAge: '$maxAge',
maxCount: '$maxCount', maxCount: '$maxCount',
truncateBefore: '$tb', truncateBefore: '$tb',
@ -11,7 +11,6 @@ const SystemMetadata = {
aclMetaWrite: '$mw', aclMetaWrite: '$mw',
userStreamAcl: '$userStreamAcl', userStreamAcl: '$userStreamAcl',
systemStreamAcl: '$systemStreamAcl' systemStreamAcl: '$systemStreamAcl'
}; });
Object.freeze(SystemMetadata);
module.exports = SystemMetadata; module.exports = SystemMetadata;

View File

@ -1,6 +1,6 @@
module.exports.metastreamOf = function(stream) { exports.metastreamOf = function(stream) {
return '$$' + stream; return '$$' + stream;
}; };
module.exports.isMetastream = function(stream) { exports.isMetastream = function(stream) {
return stream.indexOf('$$') === 0; return stream.indexOf('$$') === 0;
}; };

View File

@ -1,39 +1,45 @@
var Long = require('long');
module.exports.notNullOrEmpty = function(value, name) { module.exports.notNullOrEmpty = function(value, name) {
if (value === null) if (value === null) throw new TypeError(name + " should not be null.");
throw new TypeError(name + " should not be null."); if (value === '') throw new Error(name + " should not be empty.");
if (value === '')
throw new Error(name + " should not be empty.");
}; };
module.exports.notNull = function(value, name) { module.exports.notNull = function(value, name) {
if (value === null) if (value === null) throw new TypeError(name + " should not be null.");
throw new TypeError(name + " should not be null.");
}; };
module.exports.isInteger = function(value, name) { function isInteger(value, name) {
if (typeof value !== 'number' || value % 1 !== 0) if (typeof value !== 'number' || value % 1 !== 0) throw new TypeError(name + " should be an integer.");
throw new TypeError(name + " should be an integer."); }
module.exports.isInteger = isInteger;
module.exports.isLongOrInteger = function(value, name) {
if (typeof value === 'number') {
return isInteger(value, name);
} else if (!Long.isLong(value)) {
throw new TypeError(name + " should be a Long|number.");
}
}; };
module.exports.isArrayOf = function(expectedType, value, name) { 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.name, "."].join("")); throw new TypeError([name, " should be an array of ", expectedType.name, "."].join(""));
}
}; };
module.exports.isTypeOf = function(expectedType, value, name, nullAllowed) { module.exports.isTypeOf = function(expectedType, value, name, nullAllowed) {
if (nullAllowed && value === null) return; if (nullAllowed && value === null) return;
if (!(value instanceof expectedType)) if (!(value instanceof expectedType)) {
throw new TypeError([name, " should be of type '", expectedType.name, "'", nullAllowed ? " or null": "", "."].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) {
if (value <= 0) if (value <= 0) throw new Error(name + " should be positive.");
throw new Error(name + " should be positive.");
}; };
module.exports.nonNegative = function(value, name) { module.exports.nonNegative = function(value, name) {
if (value < 0) if (value < 0) throw new Error(name + " should be non-negative.");
throw new Error(name + " should be non-negative.");
}; };

View File

@ -0,0 +1,28 @@
function rndNext(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
function shuffle (arr, from, to) {
if (arr.length <= 1){
return arr;
}
if (!from) {
from = 0;
}
if (!to) {
to = arr.length - 1;
}
const newArr = [...arr];
if (from >= to) return;
for (var current = from; current <= to; ++current) {
var index = rndNext(current, to + 1);
var tmp = newArr[index];
newArr[index] = newArr[current];
newArr[current] = tmp;
}
return newArr;
};
module.exports = shuffle;

View File

@ -0,0 +1,165 @@
const ClusterInfo = require('./clusterInfo');
const GossipSeed = require('../../gossipSeed');
const NodeEndPoints = require('./nodeEndpoints');
const shuffle = require('../../common/utils/shuffle');
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* ClusterDiscoverer
* @constructor
* @class
* @param {Logger} log - Logger instance
* @param {Object} settings - Settings object
* @param {Object} dnsService - DNS service to perform DNS lookup
* @param {Object} httpService - HTTP service to perform http requests
*/
function ClusterDiscoverer(log, settings, dnsService, httpService) {
if (!settings.clusterDns && (!settings.seeds || settings.seeds.length === 0))
throw new Error('Both clusterDns and seeds are null/empty.');
this._log = log;
this._settings = settings;
this._dnsService = dnsService;
this._httpService = httpService;
}
/**
* Discover Cluster endpoints
* @param {Object} failedTcpEndPoint - The failed TCP endpoint which were used by the handler
* @returns {Promise.<NodeEndPoints>}
*/
ClusterDiscoverer.prototype.discover = async function (failedTcpEndPoint) {
let attempts = 0;
while (attempts++ < this._settings.maxDiscoverAttempts) {
try {
const candidates = await this._getGossipCandidates(this._settings.managerExternalHttpPort);
const gossipSeeds = candidates.filter(
(candidate) =>
!failedTcpEndPoint ||
!(candidate.endPoint.host === failedTcpEndPoint.host && candidate.endPoint.port === failedTcpEndPoint.port)
);
let gossipSeedsIndex = 0;
let clusterInfo;
do {
try {
clusterInfo = await this._clusterInfo(gossipSeeds[gossipSeedsIndex], this._settings.gossipTimeout);
if (!clusterInfo.bestNode) {
this._log.info(
`Discovering attempt ${attempts}/${this._settings.maxDiscoverAttempts} failed: no candidate found.`
);
continue;
}
} catch (err) {}
} while (++gossipSeedsIndex < gossipSeeds.length);
if (clusterInfo) {
return NodeEndPoints.createFromGossipMember(clusterInfo.bestNode);
}
} catch (err) {
this._log.info(
`Discovering attempt ${attempts}/${this._settings.maxDiscoverAttempts} failed with error: ${err}.\n${err.stack}`
);
}
await wait(this._settings.discoverDelay);
}
throw new Error(`Failed to discover candidate in ${this._settings.maxDiscoverAttempts} attempts.`);
};
/**
* Get gossip candidates either from DNS or from gossipSeeds settings
* @private
* @param {Number} managerExternalHttpPort - Http port of the manager (or the http port of the node for OSS clusters)
* @returns {Promise.<GossipSeed[]>}
*/
ClusterDiscoverer.prototype._getGossipCandidates = async function (managerExternalHttpPort) {
const gossipSeeds =
this._settings.seeds && this._settings.seeds.length > 0
? this._settings.seeds
: (await this._resolveDns(this._settings.clusterDns)).map(
(address) => new GossipSeed({ host: address, port: managerExternalHttpPort }, address, this._settings.clusterDns)
);
return shuffle(gossipSeeds);
};
/**
* Resolve the cluster DNS discovery address to retrieve belonging ip addresses
* @private
* @param {String} clusterDns - Cluster DNS discovery address
* @returns {Promise.<String[]>}
*/
ClusterDiscoverer.prototype._resolveDns = async function (clusterDns) {
const dnsOptions = {
family: 4,
hints: this._dnsService.ADDRCONFIG | this._dnsService.V4MAPPED,
all: true,
};
const result = await this._dnsService.lookup(clusterDns, dnsOptions);
if (!result || result.length === 0) {
throw new Error(`No result from dns lookup for ${clusterDns}`);
}
return result.map((address) => address.address);
};
/**
* Get cluster informations (gossip members)
* @param {GossipSeed} candidate - candidate to get informations from
* @param {Number} timeout - timeout for the http request
* @returns {Promise.<ClusterInfo>}
*/
ClusterDiscoverer.prototype._clusterInfo = async function (candidate, timeout) {
var self = this;
return new Promise((resolve, reject) => {
const options = {
host: candidate.endPoint.host,
port: candidate.endPoint.port,
path: '/gossip?format=json',
timeout: timeout,
rejectUnauthorized: self._settings.rejectUnauthorized
};
if (candidate.hostHeader) {
options.headers = {
Host: candidate.hostHeader,
};
}
const request = this._httpService.request(options, (res) => {
if (res.statusCode !== 200) {
this._log.info('Trying to get gossip from', candidate, 'failed with status code:', res.statusCode);
reject(new Error(`Gossip candidate returns a ${res.statusCode} error`));
return;
}
let result = '';
res.on('data', (chunk) => {
result += chunk.toString();
});
res.on('end', function () {
try {
result = JSON.parse(result);
} catch (e) {
reject(new Error('Unable to parse gossip response'));
}
resolve(new ClusterInfo(result.members));
});
});
request.setTimeout(timeout);
request.on('timeout', () => {
this._log.info('Trying to get gossip from', candidate, 'timed out.');
request.destroy();
reject(new Error('Connection to gossip timed out'));
});
request.on('error', (error) => {
this._log.info('Trying to get gossip from', candidate, 'errored', error);
request.destroy();
reject(new Error('Connection to gossip errored'));
});
request.end();
});
};
module.exports = ClusterDiscoverer;

View File

@ -0,0 +1,41 @@
const MemberInfo = require('./memberInfo.js');
const VNodeStates = Object.freeze({
Initializing: 0,
DiscoverLeader: 1,
Unknown: 2,
PreReplica: 3,
CatchingUp: 4,
Clone: 5,
Follower: 6,
PreLeader: 7,
Leader: 8,
Manager: 9,
ShuttingDown: 10,
Shutdown: 11,
ReadOnlyLeaderless: 12,
PreReadOnlyReplica: 13,
ReadOnlyReplica: 14,
ResigningLeader: 15,
});
function ClusterInfo(members) {
this._members = members.map(member => new MemberInfo(member));
Object.defineProperty(this, 'bestNode', {
enumerable: true,
get: function () {
return this._getBestNode();
}
});
}
ClusterInfo.prototype._getBestNode = function () {
return this._members
.filter(member => member.isAlive && member.isAllowedToConnect)
.sort(function (a, b) {
return VNodeStates[b.state] - VNodeStates[a.state];
})[0];
}
module.exports = ClusterInfo;

View File

@ -0,0 +1,73 @@
const NOT_ALLOWED_STATES = [
'Manager',
'ShuttingDown',
'Shutdown'
];
function MemberInfo(informations) {
this._instanceId = informations.instanceId;
this._timeStamp = informations.timeStamp;
this._state = informations.state;
this._isAlive = informations.isAlive;
this._internalTcpIp = informations.internalTcpIp;
this._internalTcpPort = informations.internalTcpPort;
this._internalSecureTcpPort = informations.internalSecureTcpPort;
this._externalTcpIp = informations.externalTcpIp;
this._externalTcpPort = informations.externalTcpPort;
this._externalSecureTcpPort = informations.externalSecureTcpPort;
this._internalHttpIp = informations.internalHttpIp;
this._internalHttpPort = informations.internalHttpPort;
this._externalHttpIp = informations.externalHttpIp;
this._externalHttpPort = informations.externalHttpPort;
this._lastCommitPosition = informations.lastCommitPosition;
this._writerCheckpoint = informations.writerCheckpoint;
this._chaserCheckpoint = informations.chaserCheckpoint;
this._epochPosition = informations.epochPosition;
this._epochNumber = informations.epochNumber;
this._epochId = informations.epochId;
this._nodePriority = informations.nodePriority;
Object.defineProperty(this, 'state', {
enumerable: true,
get: function () {
return this._state;
}
});
Object.defineProperty(this, 'isAllowedToConnect', {
enumerable: true,
get: function () {
return !NOT_ALLOWED_STATES.includes(this._state);
}
});
Object.defineProperty(this, 'isAlive', {
enumerable: true,
get: function () {
return this._isAlive;
}
});
Object.defineProperty(this, 'externalTcpIp', {
enumerable: true,
get: function () {
return this._externalTcpIp;
}
});
Object.defineProperty(this, 'externalTcpPort', {
enumerable: true,
get: function () {
return this._externalTcpPort;
}
});
Object.defineProperty(this, 'externalSecureTcpPort', {
enumerable: true,
get: function () {
return this._externalSecureTcpPort;
}
});
}
module.exports = MemberInfo;

View File

@ -0,0 +1,23 @@
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
}
});
}
NodeEndPoints.createFromGossipMember = function (member) {
const normTcp = { host: member.externalTcpIp, port: member.externalTcpPort };
const secTcp = member.externalSecureTcpPort > 0
? { host: member.externalTcpIp, port: member.externalSecureTcpPort }
: null;
return new NodeEndPoints(normTcp, secTcp);
}
module.exports = NodeEndPoints

View File

@ -1,254 +0,0 @@
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;

View File

@ -9,31 +9,35 @@ var SubscriptionsManager = require('./subscriptionsManager');
var VolatileSubscriptionOperation = require('../clientOperations/volatileSubscriptionOperation'); var VolatileSubscriptionOperation = require('../clientOperations/volatileSubscriptionOperation');
var ConnectToPersistentSubscriptionOperation = require('../clientOperations/connectToPersistentSubscriptionOperation'); var ConnectToPersistentSubscriptionOperation = require('../clientOperations/connectToPersistentSubscriptionOperation');
var messages = require('./messages'); var messages = require('./messages');
var ClientMessage = require('../messages/clientMessage');
var createBufferSegment = require('../common/bufferSegment');
var TcpPackage = require('../systemData/tcpPackage'); var TcpPackage = require('../systemData/tcpPackage');
var TcpCommand = require('../systemData/tcpCommand'); var TcpCommand = require('../systemData/tcpCommand');
var TcpFlags = require('../systemData/tcpFlags'); var TcpFlags = require('../systemData/tcpFlags');
var InspectionDecision = require('../systemData/inspectionDecision'); var InspectionDecision = require('../systemData/inspectionDecision');
const ConnectionState = { const ConnectionState = Object.freeze({
Init: 'init', Init: 'init',
Connecting: 'connecting', Connecting: 'connecting',
Connected: 'connected', Connected: 'connected',
Closed: 'closed' Closed: 'closed'
}; });
const ConnectingPhase = { const ConnectingPhase = Object.freeze({
Invalid: 'invalid', Invalid: 'invalid',
Reconnecting: 'reconnecting', Reconnecting: 'reconnecting',
EndPointDiscovery: 'endpointDiscovery', EndPointDiscovery: 'endpointDiscovery',
ConnectionEstablishing: 'connectionEstablishing', ConnectionEstablishing: 'connectionEstablishing',
Authentication: 'authentication', Authentication: 'authentication',
Identification: 'identification',
Connected: 'connected' Connected: 'connected'
}; });
const TimerPeriod = 200; const TimerPeriod = 200;
const TimerTickMessage = new messages.TimerTickMessage(); const TimerTickMessage = new messages.TimerTickMessage();
const EmptyGuid = '00000000-0000-0000-0000-000000000000'; const EmptyGuid = '00000000-0000-0000-0000-000000000000';
const ClientVersion = 1;
/** /**
* @private * @private
@ -46,7 +50,7 @@ function EventStoreConnectionLogicHandler(esConnection, settings) {
EventEmitter.call(this); EventEmitter.call(this);
this._esConnection = esConnection; this._esConnection = esConnection;
this._settings = settings; this._settings = settings;
this._queue = new SimpleQueuedHandler(); this._queue = new SimpleQueuedHandler(this._settings.log);
this._state = ConnectionState.Init; this._state = ConnectionState.Init;
this._connectingPhase = ConnectingPhase.Invalid; this._connectingPhase = ConnectingPhase.Invalid;
this._endpointDiscoverer = null; this._endpointDiscoverer = null;
@ -183,8 +187,7 @@ EventStoreConnectionLogicHandler.prototype._closeConnection = function(reason, e
this._logInfo("Closed. Reason: %s", reason); this._logInfo("Closed. Reason: %s", reason);
if (error) if (error) this.emit('error', error);
this.emit('error', error);
this.emit('closed', reason); this.emit('closed', reason);
}; };
@ -201,7 +204,7 @@ EventStoreConnectionLogicHandler.prototype._closeTcpConnection = function(reason
this._connection = null; this._connection = null;
}; };
var _nextSeqNo = -1; var _nextSeqNo = 0;
function createOperationItem(operation, maxRetries, timeout) { function createOperationItem(operation, maxRetries, timeout) {
var operationItem = { var operationItem = {
seqNo: _nextSeqNo++, seqNo: _nextSeqNo++,
@ -279,10 +282,11 @@ EventStoreConnectionLogicHandler.prototype._startSubscription = function(msg) {
this._state === ConnectionState.Connected ? "fire" : "enqueue", this._state === ConnectionState.Connected ? "fire" : "enqueue",
operation.constructor.name, operation, msg.maxRetries, msg.timeout); operation.constructor.name, operation, msg.maxRetries, msg.timeout);
var subscription = createSubscriptionItem(operation, msg.maxRetries, msg.timeout); var subscription = createSubscriptionItem(operation, msg.maxRetries, msg.timeout);
if (this._state === ConnectionState.Connecting) if (this._state === ConnectionState.Connecting) {
this._subscriptions.enqueueSubscription(subscription); this._subscriptions.enqueueSubscription(subscription);
else } else {
this._subscriptions.startSubscription(subscription, this._connection); this._subscriptions.startSubscription(subscription, this._connection);
}
break; break;
case ConnectionState.Closed: case ConnectionState.Closed:
msg.cb(new Error("Connection closed. Connection: " + this._esConnection.connectionName)); msg.cb(new Error("Connection closed. Connection: " + this._esConnection.connectionName));
@ -308,10 +312,11 @@ EventStoreConnectionLogicHandler.prototype._startPersistentSubscription = functi
this._state === ConnectionState.Connected ? "fire" : "enqueue", this._state === ConnectionState.Connected ? "fire" : "enqueue",
operation.constructor.name, operation, msg.maxRetries, msg.timeout); operation.constructor.name, operation, msg.maxRetries, msg.timeout);
var subscription = createSubscriptionItem(operation, msg.maxRetries, msg.timeout); var subscription = createSubscriptionItem(operation, msg.maxRetries, msg.timeout);
if (this._state === ConnectionState.Connecting) if (this._state === ConnectionState.Connecting) {
this._subscriptions.enqueueSubscription(subscription); this._subscriptions.enqueueSubscription(subscription);
else } else {
this._subscriptions.startSubscription(subscription, this._connection); this._subscriptions.startSubscription(subscription, this._connection);
}
break; break;
case ConnectionState.Closed: case ConnectionState.Closed:
msg.cb(new Error("Connection closed. " + this._esConnection.connectionName)); msg.cb(new Error("Connection closed. " + this._esConnection.connectionName));
@ -392,10 +397,21 @@ EventStoreConnectionLogicHandler.prototype._tcpConnectionEstablished = function(
} }
else else
{ {
this._goToConnectedState(); this._goToIdentifiedState();
} }
}; };
EventStoreConnectionLogicHandler.prototype._goToIdentifiedState = function() {
this._connectingPhase = ConnectingPhase.Identification;
this._identityInfo = {
correlationId: uuid.v4(),
timeStamp: Date.now()
};
var dto = new ClientMessage.IdentifyClient({version: ClientVersion, connectionName: this._esConnection.connectionName});
var buf = dto.constructor.encode(dto).finish();
this._connection.enqueueSend(new TcpPackage(TcpCommand.IdentifyClient, TcpFlags.None, this._identityInfo.correlationId, null, null, createBufferSegment(buf)))
};
EventStoreConnectionLogicHandler.prototype._goToConnectedState = function() { EventStoreConnectionLogicHandler.prototype._goToConnectedState = function() {
this._state = ConnectionState.Connected; this._state = ConnectionState.Connected;
this._connectingPhase = ConnectingPhase.Connected; this._connectingPhase = ConnectingPhase.Connected;
@ -499,6 +515,16 @@ EventStoreConnectionLogicHandler.prototype._handleTcpPackage = function(connecti
if (pkg.command === TcpCommand.NotAuthenticated) if (pkg.command === TcpCommand.NotAuthenticated)
this.emit('authenticationFailed', "Not authenticated"); this.emit('authenticationFailed', "Not authenticated");
this._goToIdentifiedState();
return;
}
}
if (pkg.command === TcpCommand.ClientIdentified)
{
if (this._state === ConnectionState.Connecting
&& this._identityInfo.correlationId === pkg.correlationId)
{
this._goToConnectedState(); this._goToConnectedState();
return; return;
} }
@ -536,8 +562,9 @@ 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;
} }
@ -584,8 +611,7 @@ EventStoreConnectionLogicHandler.prototype._reconnectTo = function(endPoints) {
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].",
this._esConnection.connectionName, endPoint, this._connection.remoteEndPoint, this._connection.localEndPoint); this._esConnection.connectionName, endPoint, this._connection.remoteEndPoint, this._connection.localEndPoint);
@ -602,26 +628,26 @@ 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...");
this._reconnInfo = {reconnectionAttempt: this._reconnInfo.reconnectionAttempt + 1, timeStamp: Date.now()}; this._reconnInfo = {reconnectionAttempt: this._reconnInfo.reconnectionAttempt + 1, timeStamp: Date.now()};
if (this._settings.maxReconnections >= 0 && this._reconnInfo.reconnectionAttempt > this._settings.maxReconnections) if (this._settings.maxReconnections >= 0 && this._reconnInfo.reconnectionAttempt > this._settings.maxReconnections) {
this._closeConnection("Reconnection limit reached."); this._closeConnection("Reconnection limit reached.");
else } else {
{
this.emit('reconnecting', {}); this.emit('reconnecting', {});
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(); if (this._clientVersion === 1) {
} this._goToIdentifiedState();
else if (this._connectingPhase === ConnectingPhase.Authentication || this._connectingPhase === ConnectingPhase.Connected) } else {
this._goToConnectedState();
}
} 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
@ -648,8 +674,7 @@ EventStoreConnectionLogicHandler.prototype._manageHeartbeats = function() {
if (this._connection === null) return; 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)
@ -682,15 +707,17 @@ EventStoreConnectionLogicHandler.prototype._manageHeartbeats = function() {
EventStoreConnectionLogicHandler.prototype._logDebug = function(message) { EventStoreConnectionLogicHandler.prototype._logDebug = function(message) {
if (!this._settings.verboseLogging) return; if (!this._settings.verboseLogging) return;
if (arguments.length > 1) if (arguments.length > 1) {
message = util.format.apply(util, Array.prototype.slice.call(arguments)); message = util.format.apply(util, Array.prototype.slice.call(arguments));
}
this._settings.log.debug("EventStoreConnection '%s': %s", this._esConnection.connectionName, message); this._settings.log.debug("EventStoreConnection '%s': %s", this._esConnection.connectionName, message);
}; };
EventStoreConnectionLogicHandler.prototype._logInfo = function(message){ EventStoreConnectionLogicHandler.prototype._logInfo = function(message){
if (arguments.length > 1) if (arguments.length > 1) {
message = util.format.apply(util, Array.prototype.slice.call(arguments)); message = util.format.apply(util, Array.prototype.slice.call(arguments));
}
this._settings.log.info("EventStoreConnection '%s': %s", this._esConnection.connectionName, message); this._settings.log.info("EventStoreConnection '%s': %s", this._esConnection.connectionName, message);
}; };

View File

@ -1,5 +1,4 @@
var util = require('util'); var util = require('util');
var ensure = require('../common/utils/ensure');
function Message() { function Message() {
} }

View File

@ -110,8 +110,7 @@ OperationsManager.prototype.checkTimeoutsAndRetry = function(connection) {
}; };
OperationsManager.prototype.scheduleOperationRetry = function(operation) { OperationsManager.prototype.scheduleOperationRetry = function(operation) {
if (!this.removeOperation(operation)) if (!this.removeOperation(operation)) return;
return;
this._logDebug("ScheduleOperationRetry for %s.", operation); this._logDebug("ScheduleOperationRetry for %s.", operation);
if (operation.maxRetries >= 0 && operation.retryCount >= operation.maxRetries) if (operation.maxRetries >= 0 && operation.retryCount >= operation.maxRetries)
@ -166,8 +165,9 @@ OperationsManager.prototype.scheduleOperation = function(operation, connection)
OperationsManager.prototype._logDebug = function(message) { OperationsManager.prototype._logDebug = function(message) {
if (!this._settings.verboseLogging) return; if (!this._settings.verboseLogging) return;
if (arguments.length > 1) if (arguments.length > 1) {
message = util.format.apply(util, Array.prototype.slice.call(arguments)); message = util.format.apply(util, Array.prototype.slice.call(arguments));
}
this._settings.log.debug("EventStoreConnection '%s': %s.", this._connectionName, message); this._settings.log.debug("EventStoreConnection '%s': %s.", this._connectionName, message);
}; };

View File

@ -1,24 +1,24 @@
function typeName(t) { function typeName(t) {
if (typeof t === 'function') if (typeof t === 'function') return t.name;
return t.name; if (typeof t === 'object') return t.constructor.name;
if (typeof t === 'object')
return t.constructor.name;
throw new TypeError('type must be a function or object, not ' + typeof t); throw new TypeError('type must be a function or object, not ' + typeof t);
} }
function SimpleQueuedHandler() { function SimpleQueuedHandler(log) {
this._handlers = {}; this._handlers = {};
this._messages = []; this._messages = [];
this._isProcessing = false; this._isProcessing = false;
this._log = log;
} }
SimpleQueuedHandler.prototype.registerHandler = function(type, handler) { SimpleQueuedHandler.prototype.registerHandler = function(type, handler) {
var typeId = typeName(type); var typeId = typeName(type);
var log = this._log;
this._handlers[typeId] = function (msg) { this._handlers[typeId] = function (msg) {
try { try {
handler(msg); handler(msg);
} catch(e) { } catch(e) {
console.log('ERROR: ', e.stack); log.error('handle for', type, 'failed:', e.stack);
} }
}; };
}; };
@ -36,8 +36,7 @@ SimpleQueuedHandler.prototype._processQueue = function() {
while(message) { while(message) {
var typeId = typeName(message); var typeId = typeName(message);
var handler = this._handlers[typeId]; var handler = this._handlers[typeId];
if (!handler) if (!handler) throw new Error("No handler registered for message " + typeId);
throw new Error("No handler registered for message " + typeId);
setImmediate(handler, message); setImmediate(handler, message);
message = this._messages.shift(); message = this._messages.shift();
} }

View File

@ -1,5 +1,3 @@
var uuid = require('uuid');
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
function isValidId(id) { function isValidId(id) {
if (typeof id !== 'string') return false; if (typeof id !== 'string') return false;
@ -26,8 +24,8 @@ function EventData(eventId, type, isJson, data, metadata) {
this.eventId = eventId; this.eventId = eventId;
this.type = type; this.type = type;
this.isJson = isJson || false; this.isJson = isJson || false;
this.data = data || new Buffer(0); this.data = data || Buffer.alloc(0);
this.metadata = metadata || new Buffer(0); this.metadata = metadata || Buffer.alloc(0);
Object.freeze(this); Object.freeze(this);
} }

View File

@ -45,8 +45,7 @@ EventStoreAllCatchUpSubscription.prototype._readEventsTill = function(
}); });
}) })
.then(function(done) { .then(function(done) {
if (done || self._shouldStop) if (done || self._shouldStop) return;
return;
return readNext(); return readNext();
}); });
} }
@ -69,10 +68,11 @@ EventStoreAllCatchUpSubscription.prototype._tryProcess = function(e) {
this._lastProcessedPosition = e.originalPosition; this._lastProcessedPosition = e.originalPosition;
processed = true; processed = true;
} }
if (this._verbose) if (this._verbose) {
this._log.debug("Catch-up Subscription to %s: %s event (%s, %d, %s @ %s).", this._log.debug("Catch-up Subscription to %s: %s event (%s, %d, %s @ %s).",
this.streamId || '<all>', processed ? "processed" : "skipping", this.streamId || '<all>', processed ? "processed" : "skipping",
e.originalEvent.eventStreamId, e.originalEvent.eventNumber, e.originalEvent.eventType, e.originalPosition); e.originalEvent.eventStreamId, e.originalEvent.eventNumber, e.originalEvent.eventType, e.originalPosition);
}
return (promise && promise.then) ? promise : Promise.resolve(); return (promise && promise.then) ? promise : Promise.resolve();
}; };

View File

@ -1,7 +1,6 @@
var util = require('util'); var util = require('util');
var SubscriptionDropReason = require('./subscriptionDropReason'); var SubscriptionDropReason = require('./subscriptionDropReason');
var results = require('./results');
const DefaultReadBatchSize = 500; const DefaultReadBatchSize = 500;
const DefaultMaxPushQueueSize = 10000; const DefaultMaxPushQueueSize = 10000;
@ -68,9 +67,9 @@ function EventStoreCatchUpSubscription(
var self = this; var self = this;
this._onReconnect = function() { this._onReconnect = function() {
if (self._verbose) self._log.debug("Catch-up Subscription to %s: recovering after reconnection.", self._streamId || '<all>');
if (self._verbose) self._log.debug("Catch-up Subscription to %s: unhooking from connection.Connected.", self._streamId || '<all>'); if (self._verbose) self._log.debug("Catch-up Subscription to %s: unhooking from connection.Connected.", self._streamId || '<all>');
self._connection.removeListener('connected', self._onReconnect); self._connection.removeListener('connected', self._onReconnect);
if (self._verbose) self._log.debug("Catch-up Subscription to %s: recovering after reconnection.", self._streamId || '<all>');
self._runSubscription(); self._runSubscription();
} }
} }
@ -128,15 +127,18 @@ EventStoreCatchUpSubscription.prototype._runSubscription = function() {
var self = this; var self = this;
this._stopped = false; this._stopped = false;
this._isDropped = false;
this._dropData = null;
if (this._verbose) this._log.debug("Catch-up Subscription to %s: pulling events...", logStreamName); if (this._verbose) this._log.debug("Catch-up Subscription to %s: pulling events...", logStreamName);
this._readEventsTill(this._connection, this._resolveLinkTos, this._userCredentials, null, null) this._readEventsTill(this._connection, this._resolveLinkTos, this._userCredentials, null, null)
.then(function() { .then(function() {
if (self._shouldStop) return; if (self._shouldStop) return;
if (self._verbose) self._log.debug("Catch-up Subscription to %s: subscribing...", logStreamName); if (self._verbose) self._log.debug("Catch-up Subscription to %s: subscribing...", logStreamName);
if (self._streamId === '') if (self._streamId === '') {
return self._connection.subscribeToAll(self._resolveLinkTos, self._enqueuePushedEvent.bind(self), self._serverSubscriptionDropped.bind(self), self._userCredentials); return self._connection.subscribeToAll(self._resolveLinkTos, self._enqueuePushedEvent.bind(self), self._serverSubscriptionDropped.bind(self), self._userCredentials);
else } else {
return self._connection.subscribeToStream(self._streamId, self._resolveLinkTos, self._enqueuePushedEvent.bind(self), self._serverSubscriptionDropped.bind(self), self._userCredentials); return self._connection.subscribeToStream(self._streamId, self._resolveLinkTos, self._enqueuePushedEvent.bind(self), self._serverSubscriptionDropped.bind(self), self._userCredentials);
}
}) })
.then(function(subscription) { .then(function(subscription) {
if (subscription === undefined) return; if (subscription === undefined) return;
@ -155,12 +157,13 @@ EventStoreCatchUpSubscription.prototype._runSubscription = function() {
return; return;
} }
if (self._verbose) self._log.debug("Catch-up Subscription to %s: processing live events...", logStreamName); if (self._verbose) self._log.debug("Catch-up Subscription to %s: processing live events...", logStreamName);
if (self._liveProcessingStarted) if (self._liveProcessingStarted) {
try { try {
self._liveProcessingStarted(self); self._liveProcessingStarted(self);
} catch(e) { } catch (e) {
self._log.error(e, "Catch-up Subscription to %s: liveProcessingStarted callback failed.", logStreamName); self._log.error(e, "Catch-up Subscription to %s: liveProcessingStarted callback failed.", logStreamName);
} }
}
if (self._verbose) self._log.debug("Catch-up Subscription to %s: hooking to connection.Connected", logStreamName); if (self._verbose) self._log.debug("Catch-up Subscription to %s: hooking to connection.Connected", logStreamName);
self._connection.on('connected', self._onReconnect); self._connection.on('connected', self._onReconnect);
self._allowProcessing = true; self._allowProcessing = true;
@ -169,10 +172,11 @@ EventStoreCatchUpSubscription.prototype._runSubscription = function() {
}; };
EventStoreCatchUpSubscription.prototype._enqueuePushedEvent = function(subscription, e) { EventStoreCatchUpSubscription.prototype._enqueuePushedEvent = function(subscription, e) {
if (this._verbose) if (this._verbose) {
this._log.debug("Catch-up Subscription to %s: event appeared (%s, %d, %s @ %s).", this._log.debug("Catch-up Subscription to %s: event appeared (%s, %d, %s @ %s).",
this._streamId || '<all>', this._streamId || '<all>',
e.originalStreamId, e.originalEventNumber, e.originalEvent.eventType, e.originalPosition); e.originalStreamId, e.originalEventNumber, e.originalEvent.eventType, e.originalPosition);
}
if (this._liveQueue.length >= this.maxPushQueueSize) if (this._liveQueue.length >= this.maxPushQueueSize)
{ {
@ -183,8 +187,7 @@ EventStoreCatchUpSubscription.prototype._enqueuePushedEvent = function(subscript
this._liveQueue.push(e); this._liveQueue.push(e);
if (this._allowProcessing) if (this._allowProcessing) this._ensureProcessingPushQueue();
this._ensureProcessingPushQueue();
}; };
EventStoreCatchUpSubscription.prototype._serverSubscriptionDropped = function(subscription, reason, err) { EventStoreCatchUpSubscription.prototype._serverSubscriptionDropped = function(subscription, reason, err) {
@ -196,8 +199,7 @@ EventStoreCatchUpSubscription.prototype._enqueueSubscriptionDropNotification = f
if (this._dropData) return; if (this._dropData) return;
this._dropData = {reason: reason, error: error}; this._dropData = {reason: reason, error: error};
this._liveQueue.push(new DropSubscriptionEvent()); this._liveQueue.push(new DropSubscriptionEvent());
if (this._allowProcessing) if (this._allowProcessing) this._ensureProcessingPushQueue();
this._ensureProcessingPushQueue();
}; };
EventStoreCatchUpSubscription.prototype._ensureProcessingPushQueue = function() { EventStoreCatchUpSubscription.prototype._ensureProcessingPushQueue = function() {
@ -244,18 +246,19 @@ EventStoreCatchUpSubscription.prototype._dropSubscription = function(reason, err
if (this._isDropped) return; if (this._isDropped) return;
this._isDropped = true; this._isDropped = true;
if (this._verbose) if (this._verbose) {
this._log.debug("Catch-up Subscription to %s: dropping subscription, reason: %s %s.", this._log.debug("Catch-up Subscription to %s: dropping subscription, reason: %s %s.",
this._streamId || '<all>', reason, error); this._streamId || '<all>', reason, error);
}
if (this._subscription) if (this._subscription) this._subscription.unsubscribe();
this._subscription.unsubscribe(); if (this._subscriptionDropped) {
if (this._subscriptionDropped)
try { try {
this._subscriptionDropped(this, reason, error); this._subscriptionDropped(this, reason, error);
} catch(e) { } catch (e) {
this._log.error(e, "Catch-up Subscription to %s: subscriptionDropped callback failed.", this._streamId || '<all>'); this._log.error(e, "Catch-up Subscription to %s: subscriptionDropped callback failed.", this._streamId || '<all>');
} }
}
this._stopped = true; this._stopped = true;
}; };

View File

@ -1,10 +1,21 @@
var EventStoreNodeConnection = require('./eventStoreNodeConnection'); var EventStoreNodeConnection = require('./eventStoreNodeConnection');
var StaticEndpointDiscoverer = require('./core/staticEndpointDiscoverer'); var StaticEndpointDiscoverer = require('./core/staticEndpointDiscoverer');
var ClusterDnsEndPointDiscoverer = require('./core/clusterDnsEndPointDiscoverer'); var ClusterDiscoverer = require('./core/cluster/clusterDiscoverer');
var NoopLogger = require('./common/log/noopLogger'); var NoopLogger = require('./common/log/noopLogger');
var ensure = require('./common/utils/ensure'); var ensure = require('./common/utils/ensure');
var defaultConnectionSettings = { const util = require('util');
const http = require('http');
const https = require('https');
const dns = require('dns');
const dnsService = {
lookup : util.promisify(dns.lookup),
ADDRCONFIG: dns.ADDRCONFIG,
V4MAPPED: dns.V4MAPPED
};
var defaultConnectionSettings = Object.freeze({
log: new NoopLogger(), log: new NoopLogger(),
verboseLogging: false, verboseLogging: false,
@ -32,9 +43,10 @@ var defaultConnectionSettings = {
// Cluster Settings // Cluster Settings
clusterDns: '', clusterDns: '',
maxDiscoverAttempts: 10, maxDiscoverAttempts: 10,
discoverDelay: 500,
externalGossipPort: 0, externalGossipPort: 0,
gossipTimeout: 1000 gossipTimeout: 1000
}; });
function merge(a,b) { function merge(a,b) {
@ -80,17 +92,18 @@ function createFromClusterDns(connectionSettings, clusterDns, externalGossipPort
var mergedSettings = merge(defaultConnectionSettings, connectionSettings || {}); var mergedSettings = merge(defaultConnectionSettings, connectionSettings || {});
var clusterSettings = { var clusterSettings = {
clusterDns: clusterDns, clusterDns: clusterDns,
gossipSeeds: null, seeds: null,
externalGossipPort: externalGossipPort, managerExternalHttpPort: externalGossipPort,
maxDiscoverAttempts: mergedSettings.maxDiscoverAttempts, maxDiscoverAttempts: mergedSettings.maxDiscoverAttempts,
gossipTimeout: mergedSettings.gossipTimeout discoverDelay: mergedSettings.discoverDelay,
gossipTimeout: mergedSettings.gossipTimeout,
rejectUnauthorized: connectionSettings.useSslConnection ? connectionSettings.validateServer : undefined
}; };
var endPointDiscoverer = new ClusterDnsEndPointDiscoverer(mergedSettings.log, var endPointDiscoverer = new ClusterDiscoverer(
clusterSettings.clusterDns, mergedSettings.log,
clusterSettings.maxDiscoverAttempts, clusterSettings,
clusterSettings.externalGossipPort, dnsService,
clusterSettings.gossipSeeds, connectionSettings.useSslConnection ? https : http
clusterSettings.gossipTimeout
); );
return new EventStoreNodeConnection(mergedSettings, clusterSettings, endPointDiscoverer, connectionName); return new EventStoreNodeConnection(mergedSettings, clusterSettings, endPointDiscoverer, connectionName);
} }
@ -101,17 +114,18 @@ function createFromGossipSeeds(connectionSettings, gossipSeeds, connectionName)
var mergedSettings = merge(defaultConnectionSettings, connectionSettings || {}); var mergedSettings = merge(defaultConnectionSettings, connectionSettings || {});
var clusterSettings = { var clusterSettings = {
clusterDns: '', clusterDns: '',
gossipSeeds: gossipSeeds, seeds: gossipSeeds,
externalGossipPort: 0, externalGossipPort: 0,
maxDiscoverAttempts: mergedSettings.maxDiscoverAttempts, maxDiscoverAttempts: mergedSettings.maxDiscoverAttempts,
gossipTimeout: mergedSettings.gossipTimeout discoverDelay: mergedSettings.discoverDelay,
gossipTimeout: mergedSettings.gossipTimeout,
rejectUnauthorized: connectionSettings.useSslConnection ? connectionSettings.validateServer : undefined
}; };
var endPointDiscoverer = new ClusterDnsEndPointDiscoverer(mergedSettings.log, var endPointDiscoverer = new ClusterDiscoverer(
clusterSettings.clusterDns, mergedSettings.log,
clusterSettings.maxDiscoverAttempts, clusterSettings,
clusterSettings.externalGossipPort, dnsService,
clusterSettings.gossipSeeds, connectionSettings.useSslConnection ? https : http
clusterSettings.gossipTimeout
); );
return new EventStoreNodeConnection(mergedSettings, clusterSettings, endPointDiscoverer, connectionName); return new EventStoreNodeConnection(mergedSettings, clusterSettings, endPointDiscoverer, connectionName);
} }
@ -121,6 +135,27 @@ function createFromGossipSeeds(connectionSettings, gossipSeeds, connectionName)
* @public * @public
* @alias createConnection * @alias createConnection
* @param {object} settings * @param {object} settings
* @param {boolean} [settings.verboseLogging=false]
* @param {number} [settings.maxQueueSize=5000]
* @param {number} [settings.maxConcurrentItems=5000]
* @param {number} [settings.maxRetries=10]
* @param {number} [settings.maxReconnections=10]
* @param {boolean} [settings.requireMaster=true]
* @param {number} [settings.reconnectionDelay=100]
* @param {number} [settings.operationTimeout=7000]
* @param {number} [settings.operationTimeoutCheckPeriod=1000]
* @param {object} [settings.defaultUserCredentials=null] - The default user credentials to use for requests
* @param {boolean} [settings.useSslConnection=false] - Whether to use SSL or not
* @param {object} [settings.targetHost=null]
* @param {boolean} [settings.validateServer=false]
* @param {boolean} [settings.failOnNoServerResponse=false]
* @param {number} [settings.heartbeatInterval=750]
* @param {number} [settings.heartbeatTimeout=1500]
* @param {number} [settings.clientConnectionTimeout=1000]
* @param {string} [settings.clusterDns='']
* @param {number} [settings.maxDiscoverAttempts=10]
* @param {number} [settings.externalGossipPort=0]
* @param {number} [settings.gossipTimeout=1000]
* @param {string|object|array} endPointOrGossipSeeds * @param {string|object|array} endPointOrGossipSeeds
* @param {string} [connectionName] * @param {string} [connectionName]
* @returns {EventStoreNodeConnection} * @returns {EventStoreNodeConnection}

View File

@ -1,5 +1,6 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid'); var uuid = require('uuid');
var Long = require('long');
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var ensure = require('./common/utils/ensure'); var ensure = require('./common/utils/ensure');
@ -101,15 +102,16 @@ EventStoreNodeConnection.prototype.close = function() {
* Delete a stream (async) * Delete a stream (async)
* @public * @public
* @param {string} stream * @param {string} stream
* @param {number} expectedVersion * @param {Long|number} expectedVersion
* @param {boolean} [hardDelete] * @param {boolean} [hardDelete]
* @param {UserCredentials} [userCredentials] * @param {UserCredentials} [userCredentials]
* @returns {Promise.<DeleteResult>} * @returns {Promise.<DeleteResult>}
*/ */
EventStoreNodeConnection.prototype.deleteStream = function(stream, expectedVersion, hardDelete, userCredentials) { EventStoreNodeConnection.prototype.deleteStream = function(stream, expectedVersion, hardDelete, userCredentials) {
ensure.notNullOrEmpty(stream, "stream"); ensure.notNullOrEmpty(stream, "stream");
ensure.isInteger(expectedVersion, "expectedVersion"); ensure.isLongOrInteger(expectedVersion, "expectedVersion");
hardDelete = !!hardDelete; expectedVersion = Long.fromValue(expectedVersion);
hardDelete = Boolean(hardDelete);
userCredentials = userCredentials || null; userCredentials = userCredentials || null;
var self = this; var self = this;
@ -129,16 +131,16 @@ EventStoreNodeConnection.prototype.deleteStream = function(stream, expectedVersi
* Append events to a stream (async) * Append events to a stream (async)
* @public * @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 {Long|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.
* @param {UserCredentials} [userCredentials] User credentials * @param {UserCredentials} [userCredentials] User credentials
* @returns {Promise.<WriteResult>} * @returns {Promise.<WriteResult>}
*/ */
EventStoreNodeConnection.prototype.appendToStream = function(stream, expectedVersion, events, userCredentials) { EventStoreNodeConnection.prototype.appendToStream = function(stream, expectedVersion, events, userCredentials) {
ensure.notNullOrEmpty(stream, "stream"); ensure.notNullOrEmpty(stream, "stream");
ensure.isInteger(expectedVersion, "expectedVersion"); ensure.isLongOrInteger(expectedVersion, "expectedVersion");
if (!Array.isArray(events)) expectedVersion = Long.fromValue(expectedVersion);
events = [events]; if (!Array.isArray(events)) events = [events];
ensure.isArrayOf(EventData, events, "events"); ensure.isArrayOf(EventData, events, "events");
userCredentials = userCredentials || null; userCredentials = userCredentials || null;
@ -158,13 +160,14 @@ EventStoreNodeConnection.prototype.appendToStream = function(stream, expectedVer
* Start a transaction (async) * Start a transaction (async)
* @public * @public
* @param {string} stream * @param {string} stream
* @param {number} expectedVersion * @param {Long|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) {
ensure.notNullOrEmpty(stream, "stream"); ensure.notNullOrEmpty(stream, "stream");
ensure.isInteger(expectedVersion, "expectedVersion"); ensure.isLongOrInteger(expectedVersion, "expectedVersion");
expectedVersion = Long.fromValue(expectedVersion);
userCredentials = userCredentials || null; userCredentials = userCredentials || null;
var self = this; var self = this;
@ -235,21 +238,20 @@ EventStoreNodeConnection.prototype.commitTransaction = function(transaction, use
* Read a single event (async) * Read a single event (async)
* @public * @public
* @param {string} stream * @param {string} stream
* @param {number} eventNumber * @param {Long|number} eventNumber
* @param {boolean} [resolveLinkTos] * @param {boolean} [resolveLinkTos]
* @param {UserCredentials} [userCredentials] * @param {UserCredentials} [userCredentials]
* @returns {Promise.<EventReadResult>} * @returns {Promise.<EventReadResult>}
*/ */
EventStoreNodeConnection.prototype.readEvent = function(stream, eventNumber, resolveLinkTos, userCredentials) { EventStoreNodeConnection.prototype.readEvent = function(stream, eventNumber, resolveLinkTos, userCredentials) {
ensure.notNullOrEmpty(stream, "stream"); ensure.notNullOrEmpty(stream, "stream");
ensure.isInteger(eventNumber, "eventNumber"); ensure.isLongOrInteger(eventNumber, "eventNumber");
if (eventNumber < -1) throw new Error("eventNumber out of range."); eventNumber = Long.fromValue(eventNumber);
resolveLinkTos = !!resolveLinkTos; resolveLinkTos = Boolean(resolveLinkTos);
userCredentials = userCredentials || null; userCredentials = userCredentials || null;
if (typeof stream !== 'string' || stream === '') throw new TypeError("stream must be an non-empty string."); if (typeof stream !== 'string' || stream === '') throw new TypeError("stream must be an non-empty string.");
if (typeof eventNumber !== 'number' || eventNumber % 1 !== 0) throw new TypeError("eventNumber must be an integer."); if (eventNumber.compare(-1) < 0) throw new Error("eventNumber out of range.");
if (eventNumber < -1) throw new Error("eventNumber out of range.");
if (resolveLinkTos && typeof resolveLinkTos !== 'boolean') throw new TypeError("resolveLinkTos must be a boolean."); if (resolveLinkTos && typeof resolveLinkTos !== 'boolean') throw new TypeError("resolveLinkTos must be a boolean.");
var self = this; var self = this;
@ -268,7 +270,7 @@ EventStoreNodeConnection.prototype.readEvent = function(stream, eventNumber, res
* Reading a specific stream forwards (async) * Reading a specific stream forwards (async)
* @public * @public
* @param {string} stream * @param {string} stream
* @param {number} start * @param {Long|number} start
* @param {number} count * @param {number} count
* @param {boolean} [resolveLinkTos] * @param {boolean} [resolveLinkTos]
* @param {UserCredentials} [userCredentials] * @param {UserCredentials} [userCredentials]
@ -278,12 +280,13 @@ EventStoreNodeConnection.prototype.readStreamEventsForward = function(
stream, start, count, resolveLinkTos, userCredentials stream, start, count, resolveLinkTos, userCredentials
) { ) {
ensure.notNullOrEmpty(stream, "stream"); ensure.notNullOrEmpty(stream, "stream");
ensure.isInteger(start, "start"); ensure.isLongOrInteger(start, "start");
start = Long.fromValue(start);
ensure.nonNegative(start, "start"); ensure.nonNegative(start, "start");
ensure.isInteger(count, "count"); ensure.isInteger(count, "count");
ensure.positive(count, "count"); ensure.positive(count, "count");
if (count > MaxReadSize) throw new Error(util.format("Count should be less than %d. For larger reads you should page.", MaxReadSize)); if (count > MaxReadSize) throw new Error(util.format("Count should be less than %d. For larger reads you should page.", MaxReadSize));
resolveLinkTos = !!resolveLinkTos; resolveLinkTos = Boolean(resolveLinkTos);
userCredentials = userCredentials || null; userCredentials = userCredentials || null;
var self = this; var self = this;
@ -302,7 +305,7 @@ EventStoreNodeConnection.prototype.readStreamEventsForward = function(
* Reading a specific stream backwards (async) * Reading a specific stream backwards (async)
* @public * @public
* @param {string} stream * @param {string} stream
* @param {number} start * @param {Long|number} start
* @param {number} count * @param {number} count
* @param {boolean} [resolveLinkTos] * @param {boolean} [resolveLinkTos]
* @param {UserCredentials} [userCredentials] * @param {UserCredentials} [userCredentials]
@ -312,11 +315,12 @@ EventStoreNodeConnection.prototype.readStreamEventsBackward = function(
stream, start, count, resolveLinkTos, userCredentials stream, start, count, resolveLinkTos, userCredentials
) { ) {
ensure.notNullOrEmpty(stream, "stream"); ensure.notNullOrEmpty(stream, "stream");
ensure.isInteger(start, "start"); ensure.isLongOrInteger(start, "start");
start = Long.fromValue(start);
ensure.isInteger(count, "count"); ensure.isInteger(count, "count");
ensure.positive(count, "count"); ensure.positive(count, "count");
if (count > MaxReadSize) throw new Error(util.format("Count should be less than %d. For larger reads you should page.", MaxReadSize)); if (count > MaxReadSize) throw new Error(util.format("Count should be less than %d. For larger reads you should page.", MaxReadSize));
resolveLinkTos = !!resolveLinkTos; resolveLinkTos = Boolean(resolveLinkTos);
userCredentials = userCredentials || null; userCredentials = userCredentials || null;
var self = this; var self = this;
@ -347,7 +351,7 @@ EventStoreNodeConnection.prototype.readAllEventsForward = function(
ensure.isInteger(maxCount, "maxCount"); ensure.isInteger(maxCount, "maxCount");
ensure.positive(maxCount, "maxCount"); ensure.positive(maxCount, "maxCount");
if (maxCount > MaxReadSize) throw new Error(util.format("Count should be less than %d. For larger reads you should page.", MaxReadSize)); if (maxCount > MaxReadSize) throw new Error(util.format("Count should be less than %d. For larger reads you should page.", MaxReadSize));
resolveLinkTos = !!resolveLinkTos; resolveLinkTos = Boolean(resolveLinkTos);
userCredentials = userCredentials || null; userCredentials = userCredentials || null;
var self = this; var self = this;
@ -378,7 +382,7 @@ EventStoreNodeConnection.prototype.readAllEventsBackward = function(
ensure.isInteger(maxCount, "maxCount"); ensure.isInteger(maxCount, "maxCount");
ensure.positive(maxCount, "maxCount"); ensure.positive(maxCount, "maxCount");
if (maxCount > MaxReadSize) throw new Error(util.format("Count should be less than %d. For larger reads you should page.", MaxReadSize)); if (maxCount > MaxReadSize) throw new Error(util.format("Count should be less than %d. For larger reads you should page.", MaxReadSize));
resolveLinkTos = !!resolveLinkTos; resolveLinkTos = Boolean(resolveLinkTos);
userCredentials = userCredentials || null; userCredentials = userCredentials || null;
var self = this; var self = this;
@ -408,8 +412,7 @@ EventStoreNodeConnection.prototype.subscribeToStream = function(
) { ) {
ensure.notNullOrEmpty(stream, "stream"); ensure.notNullOrEmpty(stream, "stream");
ensure.isTypeOf(Function, eventAppeared, "eventAppeared"); ensure.isTypeOf(Function, eventAppeared, "eventAppeared");
if (subscriptionDropped) if (subscriptionDropped) ensure.isTypeOf(Function, subscriptionDropped, "subscriptionDropped");
ensure.isTypeOf(Function, subscriptionDropped, "subscriptionDropped");
var self = this; var self = this;
return new Promise(function(resolve,reject) { return new Promise(function(resolve,reject) {
@ -428,7 +431,7 @@ EventStoreNodeConnection.prototype.subscribeToStream = function(
* Subscribe to a stream from position * Subscribe to a stream from position
* @public * @public
* @param {!string} stream * @param {!string} stream
* @param {?number} lastCheckpoint * @param {?number|Position} lastCheckpoint
* @param {!boolean} resolveLinkTos * @param {!boolean} resolveLinkTos
* @param {!function} eventAppeared * @param {!function} eventAppeared
* @param {function} [liveProcessingStarted] * @param {function} [liveProcessingStarted]
@ -442,7 +445,10 @@ EventStoreNodeConnection.prototype.subscribeToStreamFrom = function(
userCredentials, readBatchSize userCredentials, readBatchSize
) { ) {
if (typeof stream !== 'string' || stream === '') throw new TypeError("stream must be a non-empty string."); if (typeof stream !== 'string' || stream === '') throw new TypeError("stream must be a non-empty string.");
if (lastCheckpoint !== null && typeof lastCheckpoint !== 'number') throw new TypeError("lastCheckpoint must be a number or null."); if (lastCheckpoint !== null) {
ensure.isLongOrInteger(lastCheckpoint);
lastCheckpoint = Long.fromValue(lastCheckpoint);
}
if (typeof eventAppeared !== 'function') throw new TypeError("eventAppeared must be a function."); if (typeof eventAppeared !== 'function') throw new TypeError("eventAppeared must be a function.");
var catchUpSubscription = var catchUpSubscription =
@ -532,7 +538,7 @@ EventStoreNodeConnection.prototype.connectToPersistentSubscription = function(
subscriptionDropped = subscriptionDropped || null; subscriptionDropped = subscriptionDropped || null;
userCredentials = userCredentials || null; userCredentials = userCredentials || null;
bufferSize = bufferSize === undefined ? 10 : bufferSize; bufferSize = bufferSize === undefined ? 10 : bufferSize;
autoAck = autoAck === undefined ? true : !!autoAck; autoAck = autoAck === undefined ? true : Boolean(autoAck);
var subscription = new EventStorePersistentSubscription( var subscription = new EventStorePersistentSubscription(
groupName, stream, eventAppeared, subscriptionDropped, userCredentials, this._settings.log, groupName, stream, eventAppeared, subscriptionDropped, userCredentials, this._settings.log,
@ -624,7 +630,7 @@ EventStoreNodeConnection.prototype.setStreamMetadata = function() {
* Set stream metadata with raw object (async) * Set stream metadata with raw object (async)
* @public * @public
* @param {string} stream * @param {string} stream
* @param {number} expectedMetastreamVersion * @param {Long|number} expectedMetastreamVersion
* @param {object} metadata * @param {object} metadata
* @param {UserCredentials} [userCredentials] * @param {UserCredentials} [userCredentials]
* @returns {Promise.<WriteResult>} * @returns {Promise.<WriteResult>}
@ -633,15 +639,18 @@ EventStoreNodeConnection.prototype.setStreamMetadataRaw = function(
stream, expectedMetastreamVersion, metadata, userCredentials stream, expectedMetastreamVersion, metadata, userCredentials
) { ) {
ensure.notNullOrEmpty(stream, "stream"); ensure.notNullOrEmpty(stream, "stream");
if (systemStreams.isMetastream(stream)) if (systemStreams.isMetastream(stream)) {
throw new Error(util.format("Setting metadata for metastream '%s' is not supported.", stream)); throw new Error(util.format("Setting metadata for metastream '%s' is not supported.", stream));
}
ensure.isLongOrInteger(expectedMetastreamVersion, "expectedMetastreamVersion");
expectedMetastreamVersion = Long.fromValue(expectedMetastreamVersion);
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) {
if (err) return reject(err); if (err) return reject(err);
resolve(result); resolve(result);
} }
var data = metadata ? new Buffer(JSON.stringify(metadata)) : null; var data = metadata ? Buffer.from(JSON.stringify(metadata)) : null;
var metaevent = new EventData(uuid.v4(), systemEventTypes.StreamMetadata, true, data, null); var metaevent = new EventData(uuid.v4(), systemEventTypes.StreamMetadata, true, data, null);
self._enqueueOperation( self._enqueueOperation(
new AppendToStreamOperation(self._settings.log, cb, self._settings.requireMaster, new AppendToStreamOperation(self._settings.log, cb, self._settings.requireMaster,
@ -677,12 +686,12 @@ EventStoreNodeConnection.prototype.getStreamMetadataRaw = function(stream, userC
var evnt = res.event.originalEvent; var evnt = res.event.originalEvent;
var version = evnt ? evnt.eventNumber : -1; var version = evnt ? evnt.eventNumber : -1;
var data = evnt ? JSON.parse(evnt.data.toString()) : null; var data = evnt ? JSON.parse(evnt.data.toString()) : null;
return new results.RawStreamMetadataResult(stream, false, version, data); return new results.RawStreamMetadataResult(stream, false, Long.fromValue(version), data);
case results.EventReadStatus.NotFound: case results.EventReadStatus.NotFound:
case results.EventReadStatus.NoStream: case results.EventReadStatus.NoStream:
return new results.RawStreamMetadataResult(stream, false, -1, null); return new results.RawStreamMetadataResult(stream, false, Long.fromValue(-1), null);
case results.EventReadStatus.StreamDeleted: case results.EventReadStatus.StreamDeleted:
return new results.RawStreamMetadataResult(stream, true, 0x7fffffff, null); return new results.RawStreamMetadataResult(stream, true, Long.fromValue(0x7fffffff), null);
default: default:
throw new Error(util.format("Unexpected ReadEventResult: %s.", res.status)); throw new Error(util.format("Unexpected ReadEventResult: %s.", res.status));
} }
@ -703,7 +712,7 @@ EventStoreNodeConnection.prototype._enqueueOperation = function(operation) {
var message = new messages.StartOperationMessage(operation, self._settings.maxRetries, self._settings.operationTimeout); var message = new messages.StartOperationMessage(operation, self._settings.maxRetries, self._settings.operationTimeout);
function tryEnqueue() { function tryEnqueue() {
if (self._handler.totalOperationCount >= self._settings.maxQueueSize) { if (self._handler.totalOperationCount >= self._settings.maxQueueSize) {
setImmediate(tryEnqueue); setTimeout(tryEnqueue, 0);
return; return;
} }
self._handler.enqueueMessage(message); self._handler.enqueueMessage(message);

View File

@ -38,7 +38,7 @@ EventStorePersistentSubscriptionBase.prototype.start = function() {
return this._startSubscription(this._subscriptionId, this._streamId, this._bufferSize, this._userCredentials, return 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._log.debug('Subscription started.');
self._subscription = subscription; self._subscription = subscription;
return self; return self;
}); });
@ -56,8 +56,7 @@ EventStorePersistentSubscriptionBase.prototype.acknowledge = function(events) {
ensure.notNull(events, "events"); ensure.notNull(events, "events");
if (this._subscription === null) throw new Error("Invalid operation. Subscription is not active yet."); if (this._subscription === null) throw new Error("Invalid operation. Subscription is not active yet.");
if (!Array.isArray(events)) if (!Array.isArray(events)) events = [events];
events = [events];
var ids = events.map(function(x) { return x.originalEvent.eventId; }); var ids = events.map(function(x) { return x.originalEvent.eventId; });
this._subscription.notifyEventsProcessed(ids); this._subscription.notifyEventsProcessed(ids);
}; };
@ -73,8 +72,7 @@ EventStorePersistentSubscriptionBase.prototype.fail = function(events, action, r
ensure.notNull(reason, "reason"); ensure.notNull(reason, "reason");
if (this._subscription === null) throw new Error("Invalid operation. Subscription is not active yet."); if (this._subscription === null) throw new Error("Invalid operation. Subscription is not active yet.");
if (!Array.isArray(events)) if (!Array.isArray(events)) events = [events];
events = [events];
var ids = events.map(function(x) { return x.originalEvent.eventId; }); var ids = events.map(function(x) { return x.originalEvent.eventId; });
this._subscription.notifyEventsFailed(ids, action, reason); this._subscription.notifyEventsFailed(ids, action, reason);
}; };
@ -145,12 +143,12 @@ EventStorePersistentSubscriptionBase.prototype._processQueue = function() {
return self._eventAppeared(self, ev); return self._eventAppeared(self, ev);
}) })
.then(function() { .then(function() {
if(self._autoAck) if(self._autoAck) self._subscription.notifyEventsProcessed([ev.originalEvent.eventId]);
self._subscription.notifyEventsProcessed([ev.originalEvent.eventId]); if (self._verbose) {
if (self._verbose)
self._log.debug("Persistent Subscription to %s: processed event (%s, %d, %s @ %d).", self._log.debug("Persistent Subscription to %s: processed event (%s, %d, %s @ %d).",
self._streamId, ev.originalEvent.eventStreamId, ev.originalEvent.eventNumber, ev.originalEvent.eventType, self._streamId, ev.originalEvent.eventStreamId, ev.originalEvent.eventNumber, ev.originalEvent.eventType,
ev.originalEventNumber); ev.originalEventNumber);
}
return false; return false;
}, function(err) { }, function(err) {
//TODO GFY should we autonak here? //TODO GFY should we autonak here?
@ -168,12 +166,12 @@ EventStorePersistentSubscriptionBase.prototype._dropSubscription = function(reas
if (!this._isDropped) if (!this._isDropped)
{ {
this._isDropped = true; this._isDropped = true;
if (this._verbose) if (this._verbose) {
this._log.debug("Persistent Subscription to %s: dropping subscription, reason: %s %s.", this._log.debug("Persistent Subscription to %s: dropping subscription, reason: %s %s.",
this._streamId, reason, error); this._streamId, reason, error);
}
if (this._subscription !== null) if (this._subscription !== null) this._subscription.unsubscribe();
this._subscription.unsubscribe();
if (this._subscriptionDropped !== null) { if (this._subscriptionDropped !== null) {
try { try {
this._subscriptionDropped(this, reason, error); this._subscriptionDropped(this, reason, error);

View File

@ -1,4 +1,5 @@
var util = require('util'); var util = require('util');
var Long = require('long');
var EventStoreCatchUpSubscription = require('./eventStoreCatchUpSubscription'); var EventStoreCatchUpSubscription = require('./eventStoreCatchUpSubscription');
var SliceReadStatus = require('./sliceReadStatus'); var SliceReadStatus = require('./sliceReadStatus');
@ -14,8 +15,8 @@ function EventStoreStreamCatchUpSubscription(
//Ensure.NotNullOrEmpty(streamId, "streamId"); //Ensure.NotNullOrEmpty(streamId, "streamId");
this._lastProcessedEventNumber = fromEventNumberExclusive === null ? -1 : fromEventNumberExclusive; this._lastProcessedEventNumber = fromEventNumberExclusive === null ? Long.fromNumber(-1) : fromEventNumberExclusive;
this._nextReadEventNumber = fromEventNumberExclusive === null ? 0 : fromEventNumberExclusive + 1; this._nextReadEventNumber = fromEventNumberExclusive === null ? Long.fromNumber(0) : fromEventNumberExclusive.add(1);
} }
util.inherits(EventStoreStreamCatchUpSubscription, EventStoreCatchUpSubscription); util.inherits(EventStoreStreamCatchUpSubscription, EventStoreCatchUpSubscription);
@ -47,20 +48,19 @@ EventStoreStreamCatchUpSubscription.prototype._readEventsTill = function(
return processEvents(slice.events, 0) return processEvents(slice.events, 0)
.then(function() { .then(function() {
self._nextReadEventNumber = slice.nextEventNumber; self._nextReadEventNumber = slice.nextEventNumber;
var done = Promise.resolve(lastEventNumber === null ? slice.isEndOfStream : slice.nextEventNumber > lastEventNumber); var done = Promise.resolve(lastEventNumber === null ? slice.isEndOfStream : slice.nextEventNumber.compare(lastEventNumber) > 0);
if (!done && slice.isEndOfStream) if (!done && slice.isEndOfStream) return delay(100, false);
return delay(100, false);
return done; return done;
}); });
break;
case SliceReadStatus.StreamNotFound: case SliceReadStatus.StreamNotFound:
if (lastEventNumber && lastEventNumber !== -1) if (lastEventNumber && lastEventNumber.compare(-1) !== 0) {
throw new Error(util.format("Impossible: stream %s disappeared in the middle of catching up subscription.", self.streamId)); throw new Error(util.format("Impossible: stream %s disappeared in the middle of catching up subscription.", self.streamId));
}
return true; return true;
case SliceReadStatus.StreamDeleted: case SliceReadStatus.StreamDeleted:
throw new Error("Stream deleted: " + self.streamId); throw new Error("Stream deleted: " + self.streamId);
default: default:
throw new Error("Unexpected StreamEventsSlice.Status: %s.", slice.status); throw new Error(util.format("Unexpected StreamEventsSlice.Status: %s.", slice.status));
} }
}) })
.then(function(done) { .then(function(done) {
@ -71,24 +71,26 @@ EventStoreStreamCatchUpSubscription.prototype._readEventsTill = function(
} }
return readNext() return readNext()
.then(function() { .then(function() {
if (self._verbose) if (self._verbose) {
self._log.debug("Catch-up Subscription to %s: finished reading events, nextReadEventNumber = %d.", self._log.debug("Catch-up Subscription to %s: finished reading events, nextReadEventNumber = %d.",
self.isSubscribedToAll ? '<all>' : self.streamId, self._nextReadEventNumber); self.isSubscribedToAll ? '<all>' : self.streamId, self._nextReadEventNumber);
}
}); });
}; };
EventStoreStreamCatchUpSubscription.prototype._tryProcess = function(e) { EventStoreStreamCatchUpSubscription.prototype._tryProcess = function(e) {
var processed = false; var processed = false;
var promise; var promise;
if (e.originalEventNumber > this._lastProcessedEventNumber) { if (e.originalEventNumber.compare(this._lastProcessedEventNumber) > 0) {
promise = this._eventAppeared(this, e); promise = this._eventAppeared(this, e);
this._lastProcessedEventNumber = e.originalEventNumber; this._lastProcessedEventNumber = e.originalEventNumber;
processed = true; processed = true;
} }
if (this._verbose) if (this._verbose) {
this._log.debug("Catch-up Subscription to %s: %s event (%s, %d, %s @ %d).", this._log.debug("Catch-up Subscription to %s: %s event (%s, %d, %s @ %d).",
this.isSubscribedToAll ? '<all>' : this.streamId, processed ? "processed" : "skipping", this.isSubscribedToAll ? '<all>' : this.streamId, processed ? "processed" : "skipping",
e.originalEvent.eventStreamId, e.originalEvent.eventNumber, e.originalEvent.eventType, e.originalEventNumber) e.originalEvent.eventStreamId, e.originalEvent.eventNumber, e.originalEvent.eventType, e.originalEventNumber);
}
return (promise && promise.then) ? promise : Promise.resolve(); return (promise && promise.then) ? promise : Promise.resolve();
}; };

View File

@ -1,6 +1,9 @@
module.exports = function GossipSeed(endPoint, hostName) { function GossipSeed(endPoint, hostName, hostHeader) {
if (typeof endPoint !== 'object' || !endPoint.host || !endPoint.port) throw new TypeError('endPoint must be have host and port properties.'); if (typeof endPoint !== 'object' || !endPoint.host || !endPoint.port) throw new TypeError('endPoint must be have host and port properties.');
this.endPoint = endPoint; this.endPoint = endPoint;
this.hostName = hostName; this.hostName = hostName;
this.hostHeader = hostHeader;
Object.freeze(this); Object.freeze(this);
}; }
module.exports = GossipSeed;

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,5 @@
syntax = "proto3";
package EventStore.Client.Messages; package EventStore.Client.Messages;
enum OperationResult enum OperationResult
@ -13,113 +15,115 @@ enum OperationResult
} }
message NewEvent { message NewEvent {
required bytes event_id = 1; bytes event_id = 1;
required string event_type = 2; string event_type = 2;
required int32 data_content_type = 3; int32 data_content_type = 3;
required int32 metadata_content_type = 4; int32 metadata_content_type = 4;
required bytes data = 5; bytes data = 5;
optional bytes metadata = 6; bytes metadata = 6;
} }
message EventRecord { message EventRecord {
required string event_stream_id = 1; string event_stream_id = 1;
required int32 event_number = 2; int64 event_number = 2;
required bytes event_id = 3; bytes event_id = 3;
required string event_type = 4; string event_type = 4;
required int32 data_content_type = 5; int32 data_content_type = 5;
required int32 metadata_content_type = 6; int32 metadata_content_type = 6;
required bytes data = 7; bytes data = 7;
optional bytes metadata = 8; bytes metadata = 8;
optional int64 created = 9; int64 created = 9;
optional int64 created_epoch = 10; int64 created_epoch = 10;
} }
message ResolvedIndexedEvent { message ResolvedIndexedEvent {
required EventRecord event = 1; EventRecord event = 1;
optional EventRecord link = 2; EventRecord link = 2;
} }
message ResolvedEvent { message ResolvedEvent {
required EventRecord event = 1; EventRecord event = 1;
optional EventRecord link = 2; EventRecord link = 2;
required int64 commit_position = 3; int64 commit_position = 3;
required int64 prepare_position = 4; int64 prepare_position = 4;
} }
message WriteEvents { message WriteEvents {
required string event_stream_id = 1; string event_stream_id = 1;
required int32 expected_version = 2; int64 expected_version = 2;
repeated NewEvent events = 3; repeated NewEvent events = 3;
required bool require_master = 4; bool require_leader = 4;
} }
message WriteEventsCompleted { message WriteEventsCompleted {
required OperationResult result = 1; OperationResult result = 1;
optional string message = 2; string message = 2;
required int32 first_event_number = 3; int64 first_event_number = 3;
required int32 last_event_number = 4; int64 last_event_number = 4;
optional int64 prepare_position = 5; int64 prepare_position = 5;
optional int64 commit_position = 6; int64 commit_position = 6;
int64 current_version = 7;
} }
message DeleteStream { message DeleteStream {
required string event_stream_id = 1; string event_stream_id = 1;
required int32 expected_version = 2; int64 expected_version = 2;
required bool require_master = 3; bool require_leader = 3;
optional bool hard_delete = 4; bool hard_delete = 4;
} }
message DeleteStreamCompleted { message DeleteStreamCompleted {
required OperationResult result = 1; OperationResult result = 1;
optional string message = 2; string message = 2;
optional int64 prepare_position = 3; int64 prepare_position = 3;
optional int64 commit_position = 4; int64 commit_position = 4;
int64 current_version = 5;
} }
message TransactionStart { message TransactionStart {
required string event_stream_id = 1; string event_stream_id = 1;
required int32 expected_version = 2; int64 expected_version = 2;
required bool require_master = 3; bool require_leader = 3;
} }
message TransactionStartCompleted { message TransactionStartCompleted {
required int64 transaction_id = 1; int64 transaction_id = 1;
required OperationResult result = 2; OperationResult result = 2;
optional string message = 3; string message = 3;
} }
message TransactionWrite { message TransactionWrite {
required int64 transaction_id = 1; int64 transaction_id = 1;
repeated NewEvent events = 2; repeated NewEvent events = 2;
required bool require_master = 3; bool require_leader = 3;
} }
message TransactionWriteCompleted { message TransactionWriteCompleted {
required int64 transaction_id = 1; int64 transaction_id = 1;
required OperationResult result = 2; OperationResult result = 2;
optional string message = 3; string message = 3;
} }
message TransactionCommit { message TransactionCommit {
required int64 transaction_id = 1; int64 transaction_id = 1;
required bool require_master = 2; bool require_leader = 2;
} }
message TransactionCommitCompleted { message TransactionCommitCompleted {
required int64 transaction_id = 1; int64 transaction_id = 1;
required OperationResult result = 2; OperationResult result = 2;
optional string message = 3; string message = 3;
required int32 first_event_number = 4; int64 first_event_number = 4;
required int32 last_event_number = 5; int64 last_event_number = 5;
optional int64 prepare_position = 6; int64 prepare_position = 6;
optional int64 commit_position = 7; int64 commit_position = 7;
} }
message ReadEvent { message ReadEvent {
required string event_stream_id = 1; string event_stream_id = 1;
required int32 event_number = 2; int64 event_number = 2;
required bool resolve_link_tos = 3; bool resolve_link_tos = 3;
required bool require_master = 4; bool require_leader = 4;
} }
message ReadEventCompleted { message ReadEventCompleted {
@ -133,18 +137,18 @@ message ReadEventCompleted {
AccessDenied = 5; AccessDenied = 5;
} }
required ReadEventResult result = 1; ReadEventResult result = 1;
required ResolvedIndexedEvent event = 2; ResolvedIndexedEvent event = 2;
optional string error = 3; string error = 3;
} }
message ReadStreamEvents { message ReadStreamEvents {
required string event_stream_id = 1; string event_stream_id = 1;
required int32 from_event_number = 2; int64 from_event_number = 2;
required int32 max_count = 3; int32 max_count = 3;
required bool resolve_link_tos = 4; bool resolve_link_tos = 4;
required bool require_master = 5; bool require_leader = 5;
} }
message ReadStreamEventsCompleted { message ReadStreamEventsCompleted {
@ -159,21 +163,21 @@ message ReadStreamEventsCompleted {
} }
repeated ResolvedIndexedEvent events = 1; repeated ResolvedIndexedEvent events = 1;
required ReadStreamResult result = 2; ReadStreamResult result = 2;
required int32 next_event_number = 3; int64 next_event_number = 3;
required int32 last_event_number = 4; int64 last_event_number = 4;
required bool is_end_of_stream = 5; bool is_end_of_stream = 5;
required int64 last_commit_position = 6; int64 last_commit_position = 6;
optional string error = 7; string error = 7;
} }
message ReadAllEvents { message ReadAllEvents {
required int64 commit_position = 1; int64 commit_position = 1;
required int64 prepare_position = 2; int64 prepare_position = 2;
required int32 max_count = 3; int32 max_count = 3;
required bool resolve_link_tos = 4; bool resolve_link_tos = 4;
required bool require_master = 5; bool require_leader = 5;
} }
message ReadAllEventsCompleted { message ReadAllEventsCompleted {
@ -185,57 +189,105 @@ message ReadAllEventsCompleted {
AccessDenied = 3; AccessDenied = 3;
} }
required int64 commit_position = 1; int64 commit_position = 1;
required int64 prepare_position = 2; int64 prepare_position = 2;
repeated ResolvedEvent events = 3; repeated ResolvedEvent events = 3;
required int64 next_commit_position = 4; int64 next_commit_position = 4;
required int64 next_prepare_position = 5; int64 next_prepare_position = 5;
optional ReadAllResult result = 6 [default = Success]; ReadAllResult result = 6;
optional string error = 7; string error = 7;
}
message Filter{
enum FilterContext {
StreamId = 0;
EventType = 1;
}
enum FilterType {
Regex = 0;
Prefix = 1;
}
FilterContext context = 1;
FilterType type = 2;
repeated string data = 3;
}
message FilteredReadAllEvents {
int64 commit_position = 1;
int64 prepare_position = 2;
int32 max_count = 3;
int32 max_search_window = 4;
bool resolve_link_tos = 5;
bool require_leader = 6;
Filter filter = 7;
}
message FilteredReadAllEventsCompleted {
enum FilteredReadAllResult {
Success = 0;
NotModified = 1;
Error = 2;
AccessDenied = 3;
}
int64 commit_position = 1;
int64 prepare_position = 2;
repeated ResolvedEvent events = 3;
int64 next_commit_position = 4;
int64 next_prepare_position = 5;
bool is_end_of_stream = 6;
FilteredReadAllResult result = 7;
string error = 8;
} }
message CreatePersistentSubscription { message CreatePersistentSubscription {
required string subscription_group_name = 1; string subscription_group_name = 1;
required string event_stream_id = 2; string event_stream_id = 2;
required bool resolve_link_tos = 3; bool resolve_link_tos = 3;
required int32 start_from = 4; int64 start_from = 4;
required int32 message_timeout_milliseconds = 5; int32 message_timeout_milliseconds = 5;
required bool record_statistics = 6; bool record_statistics = 6;
required int32 live_buffer_size = 7; int32 live_buffer_size = 7;
required int32 read_batch_size = 8; int32 read_batch_size = 8;
required int32 buffer_size = 9; int32 buffer_size = 9;
required int32 max_retry_count = 10; int32 max_retry_count = 10;
required bool prefer_round_robin = 11; bool prefer_round_robin = 11;
required int32 checkpoint_after_time = 12; int32 checkpoint_after_time = 12;
required int32 checkpoint_max_count = 13; int32 checkpoint_max_count = 13;
required int32 checkpoint_min_count = 14; int32 checkpoint_min_count = 14;
required int32 subscriber_max_count = 15; int32 subscriber_max_count = 15;
optional string named_consumer_strategy = 16; string named_consumer_strategy = 16;
} }
message DeletePersistentSubscription { message DeletePersistentSubscription {
required string subscription_group_name = 1; string subscription_group_name = 1;
required string event_stream_id = 2; string event_stream_id = 2;
} }
message UpdatePersistentSubscription { message UpdatePersistentSubscription {
required string subscription_group_name = 1; string subscription_group_name = 1;
required string event_stream_id = 2; string event_stream_id = 2;
required bool resolve_link_tos = 3; bool resolve_link_tos = 3;
required int32 start_from = 4; int64 start_from = 4;
required int32 message_timeout_milliseconds = 5; int32 message_timeout_milliseconds = 5;
required bool record_statistics = 6; bool record_statistics = 6;
required int32 live_buffer_size = 7; int32 live_buffer_size = 7;
required int32 read_batch_size = 8; int32 read_batch_size = 8;
required int32 buffer_size = 9; int32 buffer_size = 9;
required int32 max_retry_count = 10; int32 max_retry_count = 10;
required bool prefer_round_robin = 11; bool prefer_round_robin = 11;
required int32 checkpoint_after_time = 12; int32 checkpoint_after_time = 12;
required int32 checkpoint_max_count = 13; int32 checkpoint_max_count = 13;
required int32 checkpoint_min_count = 14; int32 checkpoint_min_count = 14;
required int32 subscriber_max_count = 15; int32 subscriber_max_count = 15;
optional string named_consumer_strategy = 16; string named_consumer_strategy = 16;
} }
message UpdatePersistentSubscriptionCompleted { message UpdatePersistentSubscriptionCompleted {
@ -245,8 +297,8 @@ message UpdatePersistentSubscriptionCompleted {
Fail = 2; Fail = 2;
AccessDenied=3; AccessDenied=3;
} }
required UpdatePersistentSubscriptionResult result = 1 [default = Success]; UpdatePersistentSubscriptionResult result = 1;
optional string reason = 2; string reason = 2;
} }
message CreatePersistentSubscriptionCompleted { message CreatePersistentSubscriptionCompleted {
@ -256,8 +308,8 @@ message CreatePersistentSubscriptionCompleted {
Fail = 2; Fail = 2;
AccessDenied=3; AccessDenied=3;
} }
required CreatePersistentSubscriptionResult result = 1 [default = Success]; CreatePersistentSubscriptionResult result = 1;
optional string reason = 2; string reason = 2;
} }
message DeletePersistentSubscriptionCompleted { message DeletePersistentSubscriptionCompleted {
@ -267,19 +319,19 @@ message DeletePersistentSubscriptionCompleted {
Fail = 2; Fail = 2;
AccessDenied = 3; AccessDenied = 3;
} }
required DeletePersistentSubscriptionResult result = 1 [default = Success]; DeletePersistentSubscriptionResult result = 1;
optional string reason = 2; string reason = 2;
} }
message ConnectToPersistentSubscription { message ConnectToPersistentSubscription {
required string subscription_id = 1; string subscription_id = 1;
required string event_stream_id = 2; string event_stream_id = 2;
required int32 allowed_in_flight_messages = 3; int32 allowed_in_flight_messages = 3;
} }
message PersistentSubscriptionAckEvents { message PersistentSubscriptionAckEvents {
required string subscription_id = 1; string subscription_id = 1;
repeated bytes processed_event_ids = 2; repeated bytes processed_event_ids = 2;
} }
@ -292,34 +344,47 @@ message PersistentSubscriptionNakEvents {
Stop = 4; Stop = 4;
} }
required string subscription_id = 1; string subscription_id = 1;
repeated bytes processed_event_ids = 2; repeated bytes processed_event_ids = 2;
optional string message = 3; string message = 3;
required NakAction action = 4 [default = Unknown]; NakAction action = 4;
} }
message PersistentSubscriptionConfirmation { message PersistentSubscriptionConfirmation {
required int64 last_commit_position = 1; int64 last_commit_position = 1;
required string subscription_id = 2; string subscription_id = 2;
optional int32 last_event_number = 3; int64 last_event_number = 3;
} }
message PersistentSubscriptionStreamEventAppeared { message PersistentSubscriptionStreamEventAppeared {
required ResolvedIndexedEvent event = 1; ResolvedIndexedEvent event = 1;
int32 retryCount = 2;
} }
message SubscribeToStream { message SubscribeToStream {
required string event_stream_id = 1; string event_stream_id = 1;
required bool resolve_link_tos = 2; bool resolve_link_tos = 2;
}
message FilteredSubscribeToStream {
string event_stream_id = 1;
bool resolve_link_tos = 2;
Filter filter = 3;
int32 checkpoint_interval = 4;
}
message CheckpointReached {
int64 commit_position = 1;
int64 prepare_position = 2;
} }
message SubscriptionConfirmation { message SubscriptionConfirmation {
required int64 last_commit_position = 1; int64 last_commit_position = 1;
optional int32 last_event_number = 2; int64 last_event_number = 2;
} }
message StreamEventAppeared { message StreamEventAppeared {
required ResolvedEvent event = 1; ResolvedEvent event = 1;
} }
message UnsubscribeFromStream { message UnsubscribeFromStream {
@ -335,7 +400,7 @@ message SubscriptionDropped {
SubscriberMaxCountReached=4; SubscriberMaxCountReached=4;
} }
optional SubscriptionDropReason reason = 1 [default = Unsubscribed]; SubscriptionDropReason reason = 1;
} }
message NotHandled { message NotHandled {
@ -343,35 +408,43 @@ message NotHandled {
enum NotHandledReason { enum NotHandledReason {
NotReady = 0; NotReady = 0;
TooBusy = 1; TooBusy = 1;
NotMaster = 2; NotLeader = 2;
IsReadOnly = 3;
} }
required NotHandledReason reason = 1; NotHandledReason reason = 1;
optional bytes additional_info = 2; bytes additional_info = 2;
message MasterInfo { message LeaderInfo {
required string external_tcp_address = 1; string external_tcp_address = 1;
required int32 external_tcp_port = 2; int32 external_tcp_port = 2;
required string external_http_address = 3; string http_address = 3;
required int32 external_http_port = 4; int32 http_port = 4;
optional string external_secure_tcp_address = 5; string external_secure_tcp_address = 5;
optional int32 external_secure_tcp_port = 6; int32 external_secure_tcp_port = 6;
} }
} }
message ScavengeDatabase { message ScavengeDatabase {
} }
message ScavengeDatabaseCompleted { message ScavengeDatabaseResponse {
enum ScavengeResult { enum ScavengeResult {
Success = 0; Started = 0;
InProgress = 1; InProgress = 1;
Failed = 2; Unauthorized = 2;
} }
required ScavengeResult result = 1; ScavengeResult result = 1;
optional string error = 2; string scavengeId = 2;
required int32 total_time_ms = 3;
required int64 total_space_saved = 4; }
message IdentifyClient {
int32 version = 1;
string connection_name = 2;
}
message ClientIdentified {
} }

View File

@ -1,14 +1,15 @@
const PersistentSubscriptionNakEventAction = { const PersistentSubscriptionNakEventAction = Object.freeze({
Unknown: 0, Unknown: 0,
Park: 1, Park: 1,
Retry: 2, Retry: 2,
Skip: 3, Skip: 3,
Stop: 4 Stop: 4,
}; isValid: function(value) {
for(var k in PersistentSubscriptionNakEventAction) {
if (PersistentSubscriptionNakEventAction[k] === value) return true;
}
return false;
}
});
module.exports = PersistentSubscriptionNakEventAction; module.exports = PersistentSubscriptionNakEventAction;
module.exports.isValid = function(value) {
for(var k in PersistentSubscriptionNakEventAction)
if (PersistentSubscriptionNakEventAction[k] === value) return true;
return false;
};

View File

@ -1,4 +1,6 @@
var SystemConsumerStrategies = require('./systemConsumerStrategies'); var SystemConsumerStrategies = require('./systemConsumerStrategies');
var ensure = require('./common/utils/ensure');
var Long = require('long');
function PersistentSubscriptionSettings( function PersistentSubscriptionSettings(
resolveLinkTos, startFrom, extraStatistics, messageTimeout, resolveLinkTos, startFrom, extraStatistics, messageTimeout,
@ -6,6 +8,9 @@ function PersistentSubscriptionSettings(
checkPointAfter, minCheckPointCount, maxCheckPointCount, checkPointAfter, minCheckPointCount, maxCheckPointCount,
maxSubscriberCount, namedConsumerStrategy maxSubscriberCount, namedConsumerStrategy
) { ) {
ensure.isLongOrInteger(startFrom);
startFrom = Long.fromValue(startFrom);
this.resolveLinkTos = resolveLinkTos; this.resolveLinkTos = resolveLinkTos;
this.startFrom = startFrom; this.startFrom = startFrom;
this.extraStatistics = extraStatistics; this.extraStatistics = extraStatistics;

View File

@ -1,4 +1,5 @@
const http = require('http'); const http = require('http');
const https = require('https');
const url = require('url'); const url = require('url');
const util = require('util'); const util = require('util');
const ProjectionCommandFailedError = require('../errors/projectionCommandFailedError'); const ProjectionCommandFailedError = require('../errors/projectionCommandFailedError');
@ -14,9 +15,10 @@ function safeParseJson(json) {
} }
} }
function ProjectionsClient(log, operationTimeout) { function ProjectionsClient(log, operationTimeout, rejectUnauthorized) {
this._log = log; this._log = log;
this._operationTimeout = operationTimeout; this._operationTimeout = operationTimeout;
this._rejectUnauthorized = typeof rejectUnauthorized === 'undefined' ? true : !!rejectUnauthorized;
} }
ProjectionsClient.prototype.enable = function(httpEndPoint, name, userCredentials) { ProjectionsClient.prototype.enable = function(httpEndPoint, name, userCredentials) {
@ -31,6 +33,10 @@ ProjectionsClient.prototype.abort = function(httpEndPoint, name, userCredentials
return this.sendPost(httpEndPoint + '/projection/' + name + '/command/abort', '', userCredentials, HTTP_OK); return this.sendPost(httpEndPoint + '/projection/' + name + '/command/abort', '', userCredentials, HTTP_OK);
}; };
ProjectionsClient.prototype.reset = function(httpEndPoint, name, userCredentials) {
return this.sendPost(httpEndPoint + '/projection/' + name + '/command/reset', '', userCredentials, HTTP_OK);
};
ProjectionsClient.prototype.createOneTime = function(httpEndPoint, query, userCredentials) { ProjectionsClient.prototype.createOneTime = function(httpEndPoint, query, userCredentials) {
return this.sendPost(httpEndPoint + '/projections/onetime?type=JS', query, userCredentials, HTTP_CREATED); return this.sendPost(httpEndPoint + '/projections/onetime?type=JS', query, userCredentials, HTTP_CREATED);
}; };
@ -40,7 +46,7 @@ ProjectionsClient.prototype.createTransient = function(httpEndPoint, name, query
}; };
ProjectionsClient.prototype.createContinuous = function(httpEndPoint, name, query, trackEmittedStreams, userCredentials) { ProjectionsClient.prototype.createContinuous = function(httpEndPoint, name, query, trackEmittedStreams, userCredentials) {
return this.sendPost(httpEndPoint + '/projections/continuous?name=' + name + '&type=JS&emit=1&trackEmittedStreams=' + trackEmittedStreams, query, userCredentials, HTTP_CREATED); return this.sendPost(httpEndPoint + '/projections/continuous?name=' + name + '&type=JS&emit=1&trackemittedstreams=' + trackEmittedStreams, query, userCredentials, HTTP_CREATED);
}; };
ProjectionsClient.prototype.listAll = function(httpEndPoint, userCredentials) { ProjectionsClient.prototype.listAll = function(httpEndPoint, userCredentials) {
@ -102,22 +108,26 @@ ProjectionsClient.prototype.updateQuery = function(httpEndPoint, name, query, us
return this.sendPut(httpEndPoint + '/projection/' + name + '/query?type=JS', query, userCredentials, HTTP_OK); return this.sendPut(httpEndPoint + '/projection/' + name + '/query?type=JS', query, userCredentials, HTTP_OK);
}; };
ProjectionsClient.prototype.delete = function(httpEndPoint, name, deleteEmittedStreams, userCredentials) { ProjectionsClient.prototype.delete = function(httpEndPoint, name, deleteEmittedStreams, deleteStateStream, deleteCheckpointStream, userCredentials) {
return this.sendDelete(httpEndPoint + '/projection/' + name + '?deleteEmittedStreams=' + deleteEmittedStreams, userCredentials, HTTP_OK); return this.sendDelete(httpEndPoint + '/projection/' + name + '?deleteStateStream=' + deleteStateStream + '&deleteCheckpointStream=' + deleteCheckpointStream + '&deleteEmittedStreams=' + deleteEmittedStreams, '', userCredentials, HTTP_OK);
}; };
ProjectionsClient.prototype.request = function(method, _url, data, userCredentials, expectedCode) { ProjectionsClient.prototype.request = function(method, _url, data, userCredentials, expectedCode) {
const options = url.parse(_url); const options = url.parse(_url);
const httplib = options.protocol === 'https:' ? https : http;
options.method = method; options.method = method;
if (userCredentials) { if (userCredentials) {
options.auth = [userCredentials.username, userCredentials.password].join(':'); options.auth = [userCredentials.username, userCredentials.password].join(':');
} }
if (!this._rejectUnauthorized) {
options.rejectUnauthorized = false;
}
var self = this; var self = this;
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
const timeout = setTimeout(function () { const timeout = setTimeout(function () {
reject(new Error(util.format('Request timed out for %s on %s', method, _url))) reject(new Error(util.format('Request timed out for %s on %s', method, _url)))
}, self._operationTimeout); }, self._operationTimeout);
const req = http.request(options, function (res) { const req = httplib.request(options, function (res) {
const hasExpectedCode = res.statusCode === expectedCode; const hasExpectedCode = res.statusCode === expectedCode;
var result = ''; var result = '';
res.setEncoding('utf8'); res.setEncoding('utf8');
@ -139,6 +149,7 @@ ProjectionsClient.prototype.request = function(method, _url, data, userCredentia
}); });
req.on('error', reject); req.on('error', reject);
if (data) { if (data) {
req.setHeader('Content-Length', data.length);
req.setHeader('Content-Type', 'application/json'); req.setHeader('Content-Type', 'application/json');
req.write(data); req.write(data);
} }

View File

@ -6,12 +6,13 @@ const ProjectionsClient = require('./projectionsClient');
* @param {Logger} log Instance of Logger to use for logging. * @param {Logger} log Instance of Logger to use for logging.
* @param {string} httpEndPoint HTTP endpoint of an Event Store server. * @param {string} httpEndPoint HTTP endpoint of an Event Store server.
* @param {number} operationTimeout Operation timeout in milliseconds. * @param {number} operationTimeout Operation timeout in milliseconds.
* @param {boolean} [rejectUnauthorized] Reject authorized SSL certs (if using SSL) - set to false is using self-signed certs
* @constructor * @constructor
*/ */
function ProjectionsManager(log, httpEndPoint, operationTimeout) { function ProjectionsManager(log, httpEndPoint, operationTimeout, rejectUnauthorized) {
ensure.notNull(log, "log"); ensure.notNull(log, "log");
ensure.notNull(httpEndPoint, "httpEndPoint"); ensure.notNull(httpEndPoint, "httpEndPoint");
this._client = new ProjectionsClient(log, operationTimeout); this._client = new ProjectionsClient(log, operationTimeout, rejectUnauthorized);
this._httpEndPoint = httpEndPoint; this._httpEndPoint = httpEndPoint;
} }
@ -45,6 +46,16 @@ ProjectionsManager.prototype.abort = function(name, userCredentials) {
return this._client.abort(this._httpEndPoint, name, userCredentials); return this._client.abort(this._httpEndPoint, name, userCredentials);
}; };
/**
* Reset a projection. (This will re-emit events, streams that are written to from the projection will also be soft deleted)
* @param name The name of the projection.
* @param userCredentials Credentials for a user with permission to reset a projection.
* @returns {Promise<void>}
*/
ProjectionsManager.prototype.reset = function(name, userCredentials) {
return this._client.reset(this._httpEndPoint, name, userCredentials);
};
/** /**
* Creates a one-time query. * Creates a one-time query.
* @param query The JavaScript source code for the query. * @param query The JavaScript source code for the query.
@ -190,13 +201,15 @@ ProjectionsManager.prototype.updateQuery = function(name, query, userCredentials
/** /**
* Updates the definition of a query. * Updates the definition of a query.
* @param name The name of the projection. * @param name The name of the projection.
* @param deleteEmittedStreams Whether to delete the streams that were emitted by this projection. * @param deleteEmittedStreams Whether to delete the streams that were emitted by this projection.
* @param userCredentials Credentials for a user with permission to delete a projection. * @param deleteStateStream Where to delete the state stream for this projection
* @param deleteCheckpointStream Where to delete the checkpoint stream for this projection
* @param userCredentials Credentials for a user with permission to delete a projection.
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
ProjectionsManager.prototype.delete = function(name, deleteEmittedStreams, userCredentials) { ProjectionsManager.prototype.delete = function(name, deleteEmittedStreams, deleteStateStream, deleteCheckpointStream, userCredentials) {
return this._client.delete(this._httpEndPoint, name, deleteEmittedStreams, userCredentials); return this._client.delete(this._httpEndPoint, name, deleteEmittedStreams, deleteStateStream, deleteCheckpointStream, userCredentials);
}; };
module.exports = ProjectionsManager; module.exports = ProjectionsManager;

View File

@ -1,6 +1,6 @@
const ReadDirection = { const ReadDirection = Object.freeze({
Forward: 'forward', Forward: 'forward',
Backward: 'backward' Backward: 'backward'
}; });
module.exports = ReadDirection; module.exports = ReadDirection;

View File

@ -19,10 +19,12 @@ function Position(commitPosition, preparePosition) {
} }
Position.prototype.compareTo = function(other) { Position.prototype.compareTo = function(other) {
if (this.commitPosition.lt(other.commitPosition) || (this.commitPosition.eq(other.commitPosition)&& this.preparePosition.lt(other.preparePosition))) if (this.commitPosition.lt(other.commitPosition) || (this.commitPosition.eq(other.commitPosition)&& this.preparePosition.lt(other.preparePosition))) {
return -1; return -1;
if (this.commitPosition.gt(other.commitPosition) || (this.commitPosition.eq(other.commitPosition) && this.preparePosition.gt(other.preparePosition))) }
if (this.commitPosition.gt(other.commitPosition) || (this.commitPosition.eq(other.commitPosition) && this.preparePosition.gt(other.preparePosition))) {
return 1; return 1;
}
return 0; return 0;
}; };
@ -30,22 +32,24 @@ Position.prototype.toString = function() {
return [this.commitPosition.toString(), this.preparePosition.toString()].join("/"); return [this.commitPosition.toString(), this.preparePosition.toString()].join("/");
}; };
Position.start = new Position(0,0);
Position.end = new Position(-1,-1);
const EventReadStatus = { const EventReadStatus = Object.freeze({
Success: 'success', Success: 'success',
NotFound: 'notFound', NotFound: 'notFound',
NoStream: 'noStream', NoStream: 'noStream',
StreamDeleted: 'streamDeleted' StreamDeleted: 'streamDeleted'
}; });
Object.freeze(EventReadStatus);
/** /**
* @param {object} ev * @param {object} ev
* @constructor * @constructor
* @property {string} eventStreamId * @property {string} eventStreamId
* @property {string} eventId * @property {string} eventId
* @property {number} eventNumber * @property {Long} eventNumber
* @property {string} eventType * @property {string} eventType
* @property {Date} created
* @property {number} createdEpoch * @property {number} createdEpoch
* @property {?Buffer} data * @property {?Buffer} data
* @property {?Buffer} metadata * @property {?Buffer} metadata
@ -58,8 +62,8 @@ function RecordedEvent(ev) {
this.eventType = ev.eventType; this.eventType = ev.eventType;
this.created = new Date(ev.createdEpoch ? ev.createdEpoch.toNumber() : 0); this.created = new Date(ev.createdEpoch ? ev.createdEpoch.toNumber() : 0);
this.createdEpoch = ev.createdEpoch ? ev.createdEpoch.toNumber() : 0; this.createdEpoch = ev.createdEpoch ? ev.createdEpoch.toNumber() : 0;
this.data = ev.data ? ev.data : new Buffer(0); this.data = ev.data ? ev.data : Buffer.alloc(0);
this.metadata = ev.metadata ? ev.metadata : new Buffer(0); this.metadata = ev.metadata ? ev.metadata : Buffer.alloc(0);
this.isJson = ev.dataContentType === 1; this.isJson = ev.dataContentType === 1;
Object.freeze(this); Object.freeze(this);
} }
@ -73,7 +77,7 @@ function RecordedEvent(ev) {
* @property {boolean} isResolved * @property {boolean} isResolved
* @property {?Position} originalPosition * @property {?Position} originalPosition
* @property {string} originalStreamId * @property {string} originalStreamId
* @property {number} originalEventNumber * @property {Long} originalEventNumber
*/ */
function ResolvedEvent(ev) { function ResolvedEvent(ev) {
this.event = ev.event === null ? null : new RecordedEvent(ev.event); this.event = ev.event === null ? null : new RecordedEvent(ev.event);
@ -90,12 +94,12 @@ function ResolvedEvent(ev) {
* *
* @param {string} status * @param {string} status
* @param {string} stream * @param {string} stream
* @param {number} eventNumber * @param {Long} eventNumber
* @param {object} event * @param {object} event
* @constructor * @constructor
* @property {string} status * @property {string} status
* @property {string} stream * @property {string} stream
* @property {number} eventNumber * @property {Long} eventNumber
* @property {ResolvedEvent} event * @property {ResolvedEvent} event
*/ */
function EventReadResult(status, stream, eventNumber, event) { function EventReadResult(status, stream, eventNumber, event) {
@ -110,7 +114,7 @@ function EventReadResult(status, stream, eventNumber, event) {
* @param {number} nextExpectedVersion * @param {number} nextExpectedVersion
* @param {Position} logPosition * @param {Position} logPosition
* @constructor * @constructor
* @property {number} nextExpectedVersion * @property {Long} nextExpectedVersion
* @property {Position} logPosition * @property {Position} logPosition
*/ */
function WriteResult(nextExpectedVersion, logPosition) { function WriteResult(nextExpectedVersion, logPosition) {
@ -122,20 +126,20 @@ function WriteResult(nextExpectedVersion, logPosition) {
/** /**
* @param {string} status * @param {string} status
* @param {string} stream * @param {string} stream
* @param {number} fromEventNumber * @param {Long} fromEventNumber
* @param {string} readDirection * @param {string} readDirection
* @param {object[]} events * @param {object[]} events
* @param {number} nextEventNumber * @param {Long} nextEventNumber
* @param {number} lastEventNumber * @param {Long} lastEventNumber
* @param {boolean} isEndOfStream * @param {boolean} isEndOfStream
* @constructor * @constructor
* @property {string} status * @property {string} status
* @property {string} stream * @property {string} stream
* @property {number} fromEventNumber * @property {Long} fromEventNumber
* @property {string} readDirection * @property {string} readDirection
* @property {ResolvedEvent[]} events * @property {ResolvedEvent[]} events
* @property {number} nextEventNumber * @property {Long} nextEventNumber
* @property {number} lastEventNumber * @property {Long} lastEventNumber
* @property {boolean} isEndOfStream * @property {boolean} isEndOfStream
*/ */
function StreamEventsSlice( function StreamEventsSlice(
@ -185,12 +189,12 @@ function DeleteResult(logPosition) {
/** /**
* @param {string} stream * @param {string} stream
* @param {boolean} isStreamDeleted * @param {boolean} isStreamDeleted
* @param {number} metastreamVersion * @param {Long} metastreamVersion
* @param {object} streamMetadata * @param {object} streamMetadata
* @constructor * @constructor
* @property {string} stream * @property {string} stream
* @property {boolean} isStreamDeleted * @property {boolean} isStreamDeleted
* @property {number} metastreamVersion * @property {Long} metastreamVersion
* @property {object} streamMetadata * @property {object} streamMetadata
*/ */
function RawStreamMetadataResult(stream, isStreamDeleted, metastreamVersion, streamMetadata) { function RawStreamMetadataResult(stream, isStreamDeleted, metastreamVersion, streamMetadata) {
@ -202,12 +206,11 @@ function RawStreamMetadataResult(stream, isStreamDeleted, metastreamVersion, str
Object.freeze(this); Object.freeze(this);
} }
const PersistentSubscriptionCreateStatus = { const PersistentSubscriptionCreateStatus = Object.freeze({
Success: 'success', Success: 'success',
NotFound: 'notFound', NotFound: 'notFound',
Failure: 'failure' Failure: 'failure'
}; });
Object.freeze(PersistentSubscriptionCreateStatus);
/** /**
* @param {string} status * @param {string} status
@ -219,13 +222,12 @@ function PersistentSubscriptionCreateResult(status) {
Object.freeze(this); Object.freeze(this);
} }
const PersistentSubscriptionUpdateStatus = { const PersistentSubscriptionUpdateStatus = Object.freeze({
Success: 'success', Success: 'success',
NotFound: 'notFound', NotFound: 'notFound',
Failure: 'failure', Failure: 'failure',
AccessDenied: 'accessDenied' AccessDenied: 'accessDenied'
}; });
Object.freeze(PersistentSubscriptionUpdateStatus);
/** /**
* @param {string} status * @param {string} status
@ -237,11 +239,10 @@ function PersistentSubscriptionUpdateResult(status) {
Object.freeze(this); Object.freeze(this);
} }
const PersistentSubscriptionDeleteStatus = { const PersistentSubscriptionDeleteStatus = Object.freeze({
Success: 'success', Success: 'success',
Failure: 'failure' Failure: 'failure'
}; });
Object.freeze(PersistentSubscriptionDeleteStatus);
/** /**
* @param {string} status * @param {string} status
@ -254,18 +255,18 @@ function PersistentSubscriptionDeleteResult(status) {
} }
// Exports Constructors // Exports Constructors
module.exports.Position = Position; exports.Position = Position;
module.exports.ResolvedEvent = ResolvedEvent; exports.ResolvedEvent = ResolvedEvent;
module.exports.EventReadStatus = EventReadStatus; exports.EventReadStatus = EventReadStatus;
module.exports.EventReadResult = EventReadResult; exports.EventReadResult = EventReadResult;
module.exports.WriteResult = WriteResult; exports.WriteResult = WriteResult;
module.exports.StreamEventsSlice = StreamEventsSlice; exports.StreamEventsSlice = StreamEventsSlice;
module.exports.AllEventsSlice = AllEventsSlice; exports.AllEventsSlice = AllEventsSlice;
module.exports.DeleteResult = DeleteResult; exports.DeleteResult = DeleteResult;
module.exports.RawStreamMetadataResult = RawStreamMetadataResult; exports.RawStreamMetadataResult = RawStreamMetadataResult;
module.exports.PersistentSubscriptionCreateResult = PersistentSubscriptionCreateResult; exports.PersistentSubscriptionCreateResult = PersistentSubscriptionCreateResult;
module.exports.PersistentSubscriptionCreateStatus = PersistentSubscriptionCreateStatus; exports.PersistentSubscriptionCreateStatus = PersistentSubscriptionCreateStatus;
module.exports.PersistentSubscriptionUpdateResult = PersistentSubscriptionUpdateResult; exports.PersistentSubscriptionUpdateResult = PersistentSubscriptionUpdateResult;
module.exports.PersistentSubscriptionUpdateStatus = PersistentSubscriptionUpdateStatus; exports.PersistentSubscriptionUpdateStatus = PersistentSubscriptionUpdateStatus;
module.exports.PersistentSubscriptionDeleteResult = PersistentSubscriptionDeleteResult; exports.PersistentSubscriptionDeleteResult = PersistentSubscriptionDeleteResult;
module.exports.PersistentSubscriptionDeleteStatus = PersistentSubscriptionDeleteStatus; exports.PersistentSubscriptionDeleteStatus = PersistentSubscriptionDeleteStatus;

View File

@ -1,8 +1,7 @@
const SliceReadStatus = { const SliceReadStatus = Object.freeze({
Success: 'success', Success: 'success',
StreamNotFound: 'streamNotFound', StreamNotFound: 'streamNotFound',
StreamDeleted: 'streamDeleted' StreamDeleted: 'streamDeleted'
}; });
Object.freeze(SliceReadStatus);
module.exports = SliceReadStatus; module.exports = SliceReadStatus;

View File

@ -1,4 +1,4 @@
const SubscriptionDropReason = { const SubscriptionDropReason = Object.freeze({
AccessDenied: 'accessDenied', AccessDenied: 'accessDenied',
CatchUpError: 'catchUpError', CatchUpError: 'catchUpError',
ConnectionClosed: 'connectionClosed', ConnectionClosed: 'connectionClosed',
@ -11,6 +11,6 @@ const SubscriptionDropReason = {
SubscribingError: 'subscribingError', SubscribingError: 'subscribingError',
UserInitiated: 'userInitiated', UserInitiated: 'userInitiated',
Unknown: 'unknown' Unknown: 'unknown'
}; });
module.exports = SubscriptionDropReason; module.exports = SubscriptionDropReason;

View File

@ -1,8 +1,7 @@
const SystemConsumerStrategies = { const SystemConsumerStrategies = Object.freeze({
DispatchToSingle: 'DispatchToSingle', DispatchToSingle: 'DispatchToSingle',
RoundRobin: 'RoundRobin', RoundRobin: 'RoundRobin',
Pinned: 'Pinned' Pinned: 'Pinned'
}; });
Object.freeze(SystemConsumerStrategies);
module.exports = SystemConsumerStrategies; module.exports = SystemConsumerStrategies;

View File

@ -1,9 +1,9 @@
var InspectionDecision = { var InspectionDecision = Object.freeze({
DoNothing: 'doNothing', DoNothing: 'doNothing',
EndOperation: 'endOperation', EndOperation: 'endOperation',
Retry: 'retry', Retry: 'retry',
Reconnect: 'reconnect', Reconnect: 'reconnect',
Subscribed: 'subscribed' Subscribed: 'subscribed'
}; });
module.exports = InspectionDecision; module.exports = InspectionDecision;

View File

@ -1,9 +1,7 @@
var ClientMessage = require('../messages/clientMessage'); var ClientMessage = require('../messages/clientMessage');
var SliceReadStatus = require('../sliceReadStatus'); var SliceReadStatus = require('../sliceReadStatus');
module.exports = {}; exports.convert = function(code) {
module.exports.convert = function(code) {
switch(code) { switch(code) {
case ClientMessage.ReadStreamEventsCompleted.ReadStreamResult.Success: case ClientMessage.ReadStreamEventsCompleted.ReadStreamResult.Success:
return SliceReadStatus.Success; return SliceReadStatus.Success;

View File

@ -1,4 +1,4 @@
const TcpCommand = { const TcpCommand = Object.freeze({
HeartbeatRequestCommand: 0x01, HeartbeatRequestCommand: 0x01,
HeartbeatResponseCommand: 0x02, HeartbeatResponseCommand: 0x02,
@ -71,16 +71,20 @@ const TcpCommand = {
NotHandled: 0xF1, NotHandled: 0xF1,
Authenticate: 0xF2, Authenticate: 0xF2,
Authenticated: 0xF3, Authenticated: 0xF3,
NotAuthenticated: 0xF4 NotAuthenticated: 0xF4,
}; IdentifyClient: 0xF5,
ClientIdentified: 0xF6,
getName: function(v) {
return _reverseLookup[v];
}
});
var _reverseLookup = {}; var _reverseLookup = {};
for(var n in TcpCommand) { for(var n in TcpCommand) {
if (n === 'getName') continue;
var v = TcpCommand[n]; var v = TcpCommand[n];
_reverseLookup[v] = n; _reverseLookup[v] = n;
} }
module.exports = TcpCommand; module.exports = TcpCommand;
module.exports.getName = function(v) {
return _reverseLookup[v];
};

View File

@ -1,6 +1,6 @@
const TcpFlags = { const TcpFlags = Object.freeze({
None: 0x0, None: 0x0,
Authenticated: 0x01 Authenticated: 0x01
}; });
module.exports = TcpFlags; module.exports = TcpFlags;

View File

@ -19,8 +19,7 @@ function TcpPackage(command, flags, correlationId, login, password, data) {
} }
TcpPackage.fromBufferSegment = function(data) { TcpPackage.fromBufferSegment = function(data) {
if (data.length < MandatorySize) if (data.length < MandatorySize) throw new Error("ArraySegment too short, length: " + data.length);
throw new Error("ArraySegment too short, length: " + data.length);
var command = data.buffer[data.offset + CommandOffset]; var command = data.buffer[data.offset + CommandOffset];
var flags = data.buffer[data.offset + FlagsOffset]; var flags = data.buffer[data.offset + FlagsOffset];
@ -32,13 +31,15 @@ TcpPackage.fromBufferSegment = function(data) {
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) {
throw new Error("Login length is too big, it doesn't fit into TcpPackage."); throw new Error("Login length is too big, it doesn't fit into TcpPackage.");
}
login = data.buffer.toString('utf8', data.offset + AuthOffset + 1, data.offset + AuthOffset + 1 + loginLen); login = data.buffer.toString('utf8', data.offset + AuthOffset + 1, data.offset + AuthOffset + 1 + loginLen);
var passLen = data.buffer[data.offset + AuthOffset + 1 + loginLen]; var passLen = data.buffer[data.offset + AuthOffset + 1 + loginLen];
if (AuthOffset + 1 + loginLen + 1 + passLen > data.count) if (AuthOffset + 1 + loginLen + 1 + passLen > data.count) {
throw new Error("Password length is too big, it doesn't fit into TcpPackage."); throw new Error("Password length is too big, it doesn't fit into TcpPackage.");
}
headerSize += 1 + loginLen + 1 + passLen; headerSize += 1 + loginLen + 1 + passLen;
pass = data.buffer.toString('utf8', data.offset + AuthOffset + 1 + loginLen + 1, data.offset + headerSize); pass = data.buffer.toString('utf8', data.offset + AuthOffset + 1 + loginLen + 1, data.offset + headerSize);
} }
@ -49,12 +50,12 @@ 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 = Buffer.from(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 = Buffer.from(this.password);
if (passwordBytes.length > 255) throw new Error("Password serialized length should be less than 256 bytes."); if (passwordBytes.length > 255) throw new Error("Password serialized length should be less than 256 bytes.");
var res = new Buffer(MandatorySize + 2 + loginBytes.length + passwordBytes.length + (this.data ? this.data.count : 0)); var res = Buffer.alloc(MandatorySize + 2 + loginBytes.length + passwordBytes.length + (this.data ? this.data.count : 0));
res[CommandOffset] = this.command; res[CommandOffset] = this.command;
res[FlagsOffset] = this.flags; res[FlagsOffset] = this.flags;
guidParse.parse(this.correlationId, res, CorrelationOffset); guidParse.parse(this.correlationId, res, CorrelationOffset);
@ -64,17 +65,15 @@ TcpPackage.prototype.asBuffer = function() {
res[AuthOffset + 1 + loginBytes.length] = passwordBytes.length; res[AuthOffset + 1 + loginBytes.length] = passwordBytes.length;
passwordBytes.copy(res, AuthOffset + 2 + loginBytes.length); passwordBytes.copy(res, AuthOffset + 2 + loginBytes.length);
if (this.data) if (this.data) this.data.copyTo(res, res.length - this.data.count);
this.data.copyTo(res, res.length - this.data.count);
return res; return res;
} else { } else {
var res = new Buffer(MandatorySize + (this.data ? this.data.count : 0)); var res = Buffer.alloc(MandatorySize + (this.data ? this.data.count : 0));
res[CommandOffset] = this.command; res[CommandOffset] = this.command;
res[FlagsOffset] = this.flags; res[FlagsOffset] = this.flags;
guidParse.parse(this.correlationId, res, CorrelationOffset); guidParse.parse(this.correlationId, res, CorrelationOffset);
if (this.data) if (this.data) this.data.copyTo(res, AuthOffset);
this.data.copyTo(res, AuthOffset);
return res; return res;
} }
}; };

View File

@ -31,10 +31,11 @@ LengthPrefixMessageFramer.prototype._parse = function(bytes) {
++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(''));
}
this._messageBuffer = new Buffer(this._packageLength); this._messageBuffer = Buffer.alloc(this._packageLength);
} }
} }
else else
@ -46,8 +47,9 @@ LengthPrefixMessageFramer.prototype._parse = function(bytes) {
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();
} }
} }
@ -56,7 +58,7 @@ LengthPrefixMessageFramer.prototype._parse = function(bytes) {
LengthPrefixMessageFramer.prototype.frameData = function(data) { LengthPrefixMessageFramer.prototype.frameData = function(data) {
var length = data.count; var length = data.count;
var lengthBuffer = new Buffer(HeaderLength); var lengthBuffer = Buffer.alloc(HeaderLength);
lengthBuffer.writeInt32LE(length, 0); lengthBuffer.writeInt32LE(length, 0);
return [ return [
createBufferSegment(lengthBuffer, 0, HeaderLength), createBufferSegment(lengthBuffer, 0, HeaderLength),

View File

@ -1,4 +1,5 @@
var net = require('net'); var net = require('net');
var tls = require('tls');
var createBufferSegment = require('../../common/bufferSegment'); var createBufferSegment = require('../../common/bufferSegment');
const MaxSendPacketSize = 64 * 1024; const MaxSendPacketSize = 64 * 1024;
@ -34,16 +35,15 @@ TcpConnection.prototype._initSocket = function(socket) {
this._localEndPoint = {host: socket.localAddress, port: socket.localPort}; this._localEndPoint = {host: socket.localAddress, port: socket.localPort};
this._remoteEndPoint.host = socket.remoteAddress; this._remoteEndPoint.host = socket.remoteAddress;
this._socket.on('drain', this._trySend.bind(this));
this._socket.on('error', this._processError.bind(this)); this._socket.on('error', this._processError.bind(this));
this._socket.on('drain', this._trySend.bind(this));
this._socket.on('data', this._processReceive.bind(this)); this._socket.on('data', this._processReceive.bind(this));
this._socket.on('close', this._processClose.bind(this));
this._trySend(); this._trySend();
}; };
TcpConnection.prototype.enqueueSend = function(bufSegmentArray) { TcpConnection.prototype.enqueueSend = function(bufSegmentArray) {
//console.log(bufSegmentArray);
for(var i = 0; i < bufSegmentArray.length; i++) { for(var i = 0; i < bufSegmentArray.length; i++) {
var bufSegment = bufSegmentArray[i]; var bufSegment = bufSegmentArray[i];
this._sendQueue.push(bufSegment.toBuffer()); this._sendQueue.push(bufSegment.toBuffer());
@ -58,17 +58,14 @@ TcpConnection.prototype._trySend = function() {
var buffers = []; var buffers = [];
var bytes = 0; var bytes = 0;
var sendPiece; var sendPiece;
while(sendPiece = this._sendQueue.shift()) { while((sendPiece = this._sendQueue.shift())) {
buffers.push(sendPiece); buffers.push(sendPiece);
bytes += sendPiece.length; bytes += sendPiece.length;
if (bytes > MaxSendPacketSize) if (bytes > MaxSendPacketSize) break;
break;
} }
var joinedBuffers = Buffer.concat(buffers, bytes); var joinedBuffers = Buffer.concat(buffers, bytes);
if (!this._socket.write(joinedBuffers)) { if (!this._socket.write(joinedBuffers)) return;
return;
}
setImmediate(this._trySend.bind(this)); setImmediate(this._trySend.bind(this));
}; };
@ -77,6 +74,10 @@ TcpConnection.prototype._processError = function(err) {
this._closeInternal(err, "Socket error"); this._closeInternal(err, "Socket error");
}; };
TcpConnection.prototype._processClose = function(had_error) {
this._closeInternal(had_error, "Socket closed");
};
TcpConnection.prototype._processReceive = function(buf) { TcpConnection.prototype._processReceive = function(buf) {
if (buf.length === 0) { if (buf.length === 0) {
//NotifyReceiveCompleted(0); //NotifyReceiveCompleted(0);
@ -96,8 +97,7 @@ TcpConnection.prototype.receive = function(cb) {
}; };
TcpConnection.prototype._tryDequeueReceivedData = function() { TcpConnection.prototype._tryDequeueReceivedData = function() {
if (this._receiveCallback === null || this._receiveQueue.length === 0) if (this._receiveCallback === null || this._receiveQueue.length === 0) return;
return;
var res = []; var res = [];
while(this._receiveQueue.length > 0) { while(this._receiveQueue.length > 0) {
@ -111,8 +111,9 @@ TcpConnection.prototype._tryDequeueReceivedData = function() {
callback(this, res); callback(this, res);
var bytes = 0; var bytes = 0;
for(var i=0;i<res.length;i++) for(var i=0;i<res.length;i++) {
bytes += res[i].count; bytes += res[i].count;
}
//this._pendingReceivedBytes -= bytes; //this._pendingReceivedBytes -= bytes;
}; };
@ -131,27 +132,42 @@ TcpConnection.prototype._closeInternal = function(err, reason) {
this._socket = null; this._socket = null;
} }
if (this._onConnectionClosed !== null) if (this._onConnectionClosed !== null) this._onConnectionClosed(this, err);
this._onConnectionClosed(this, err);
}; };
TcpConnection.createConnectingConnection = function( TcpConnection.createConnectingConnection = function(
log, connectionId, remoteEndPoint, connectionTimeout, log, connectionId, remoteEndPoint, ssl, targetHost, validateServer,
onConnectionEstablished, onConnectionFailed, onConnectionClosed connectionTimeout, onConnectionEstablished, onConnectionFailed, onConnectionClosed
) { ) {
var connection = new TcpConnection(log, connectionId, remoteEndPoint, onConnectionClosed); var connection = new TcpConnection(log, connectionId, remoteEndPoint, onConnectionClosed);
var socket = net.connect(remoteEndPoint.port, remoteEndPoint.host); var provider = ssl ? tls : net;
function onError(err) { var options = {
if (onConnectionFailed) servername: targetHost,
onConnectionFailed(connection, err); rejectUnauthorized: validateServer,
} port: remoteEndPoint.port,
socket.once('error', onError); host: remoteEndPoint.host,
socket.on('connect', function() { timeout: connectionTimeout
};
var socket = provider.connect(options, function() {
socket.removeListener('error', onError); socket.removeListener('error', onError);
connection._initSocket(socket); connection._initSocket(socket);
if (onConnectionEstablished) if (onConnectionEstablished) onConnectionEstablished(connection);
onConnectionEstablished(connection);
}); });
var timer = setTimeout(function(){
log.error('TcpConnection: timeout when connecting to %j in %d ms', remoteEndPoint, connectionTimeout);
connection.close();
if (onConnectionFailed) onConnectionFailed(connection, new Error('Connection failed'));
}, connectionTimeout)
socket.once('error', onError);
function onError(err) {
clearTimeout(timer);
if (onConnectionFailed) onConnectionFailed(connection, err);
}
socket.once('connect', onConnect);
function onConnect() {
log.info('TcpConnection: successfully connected to %j', remoteEndPoint);
clearTimeout(timer);
}
return connection; return connection;
}; };

View File

@ -1,5 +1,4 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid');
var LengthPrefixMessageFramer = require('./lengthPrefixMessageFramer'); var LengthPrefixMessageFramer = require('./lengthPrefixMessageFramer');
var TcpConnection = require('./tcpConnection'); var TcpConnection = require('./tcpConnection');
@ -38,15 +37,14 @@ function TcpPackageConnection(
this._framer = new LengthPrefixMessageFramer(); this._framer = new LengthPrefixMessageFramer();
this._framer.registerMessageArrivedCallback(this._incomingMessageArrived.bind(this)); this._framer.registerMessageArrivedCallback(this._incomingMessageArrived.bind(this));
//TODO ssl
var self = this; var self = this;
this._connection = TcpConnection.createConnectingConnection( this._connection = TcpConnection.createConnectingConnection(
log, log,
connectionId, connectionId,
remoteEndPoint, remoteEndPoint,
//ssl, ssl,
//targetHost, targetHost,
//validateServer, validateServer,
timeout, timeout,
function(tcpConnection) { function(tcpConnection) {
log.debug("TcpPackageConnection: connected to [%j, L%j, %s].", tcpConnection.remoteEndPoint, tcpConnection.localEndPoint, connectionId); log.debug("TcpPackageConnection: connected to [%j, L%j, %s].", tcpConnection.remoteEndPoint, tcpConnection.localEndPoint, connectionId);
@ -56,13 +54,11 @@ function TcpPackageConnection(
log.debug("TcpPackageConnection: connection to [%j, L%j, %s] failed. Error: %s.", conn.remoteEndPoint, conn.localEndPoint, connectionId, error); log.debug("TcpPackageConnection: connection to [%j, L%j, %s] failed. Error: %s.", conn.remoteEndPoint, conn.localEndPoint, connectionId, error);
connectionClosed(self, error); connectionClosed(self, error);
}, },
function (conn, had_error) { function (conn, error) {
var error; if (error === true) error = new Error('transmission error.');
if (had_error)
error = new Error('transmission error.');
log.debug("TcpPackageConnection: connection [%j, L%j, %s] was closed %s", conn.remoteEndPoint, conn.localEndPoint, log.debug("TcpPackageConnection: connection [%j, L%j, %s] was closed %s", conn.remoteEndPoint, conn.localEndPoint,
connectionId, had_error ? "with error: " + error + "." : "cleanly."); connectionId, error ? "with error: " + error.stack : "cleanly.");
connectionClosed(self, error); connectionClosed(self, error);
}); });
@ -121,27 +117,23 @@ 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);
}; };

17
src/utils/modules.js Normal file
View File

@ -0,0 +1,17 @@
exports.uncache = function uncache(id) {
var mod = require.cache[id];
if (!mod) return;
delete require.cache[id];
if (mod.children)
for(var i = 0; i < mod.children.length; i++)
uncache(mod.children[i].id);
return mod;
};
exports.recache = function recache(mod) {
if (require.cache[mod.id] === mod) return;
require.cache[mod.id] = mod;
if (mod.children)
for(var i = 0; i < mod.children.length; i++)
recache(mod.children[i]);
};

View File

@ -1,13 +1,17 @@
var uuid = require('uuid'); var uuid = require('uuid');
var client = require('../src/client'); var client = require('../lib/dist');
var Long = require('long');
var ANY_VERSION = Long.fromNumber(client.expectedVersion.any);
var NOSTREAM_VERSION = Long.fromNumber(client.expectedVersion.noStream);
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); 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, ANY_VERSION, event)
.then(function(result) { .then(function(result) {
test.areEqual("nextExpectedVersion", result.nextExpectedVersion, 0); test.areEqual("nextExpectedVersion", result.nextExpectedVersion, Long.fromNumber(0));
test.ok(result.logPosition, "No log position in result."); test.ok(result.logPosition, "No log position in result.");
test.done(); test.done();
}) })
@ -25,9 +29,9 @@ module.exports = {
else else
events.push(client.createJsonEventData(uuid.v4(), {b: Math.random(), a: uuid.v4()}, null, 'otherEvent')); events.push(client.createJsonEventData(uuid.v4(), {b: Math.random(), a: uuid.v4()}, null, 'otherEvent'));
} }
this.conn.appendToStream(this.testStreamName, client.expectedVersion.any, events) this.conn.appendToStream(this.testStreamName, ANY_VERSION, events)
.then(function(result) { .then(function(result) {
test.areEqual("result.nextExpectedVersion", result.nextExpectedVersion, expectedVersion); test.areEqual("result.nextExpectedVersion", result.nextExpectedVersion, Long.fromNumber(expectedVersion));
test.ok(result.logPosition, "No log position in result."); test.ok(result.logPosition, "No log position in result.");
test.done(); test.done();
}) })
@ -45,9 +49,9 @@ module.exports = {
else else
events.push(client.createJsonEventData(uuid.v4(), {b: Math.random(), a: uuid.v4()}, null, 'otherEvent')); events.push(client.createJsonEventData(uuid.v4(), {b: Math.random(), a: uuid.v4()}, null, 'otherEvent'));
} }
this.conn.appendToStream(this.testStreamName, client.expectedVersion.any, events) this.conn.appendToStream(this.testStreamName, ANY_VERSION, events)
.then(function(result) { .then(function(result) {
test.areEqual("result.nextExpectedVersion", result.nextExpectedVersion, expectedVersion); test.areEqual("result.nextExpectedVersion", result.nextExpectedVersion, Long.fromNumber(expectedVersion));
test.ok(result.logPosition, "No log position in result."); test.ok(result.logPosition, "No log position in result.");
test.done(); test.done();
}) })
@ -60,9 +64,9 @@ module.exports = {
const largeData = Buffer.alloc(3 * 1024 *1024, " "); const largeData = Buffer.alloc(3 * 1024 *1024, " ");
const event = client.createJsonEventData(uuid.v4(), {a: largeData.toString()}, null, 'largePayloadEvent'); const event = client.createJsonEventData(uuid.v4(), {a: largeData.toString()}, null, 'largePayloadEvent');
this.conn.appendToStream(this.testStreamName, client.expectedVersion.any, event) this.conn.appendToStream(this.testStreamName, ANY_VERSION, event)
.then(function(result) { .then(function(result) {
test.areEqual("result.nextExpectedVersion", result.nextExpectedVersion, 0); test.areEqual("result.nextExpectedVersion", result.nextExpectedVersion, Long.fromNumber(0));
test.ok(result.logPosition, "No log position in result."); test.ok(result.logPosition, "No log position in result.");
test.done(); test.done();
}) })
@ -73,7 +77,7 @@ module.exports = {
'Append To Stream Wrong Expected Version': function(test) { 'Append To Stream Wrong Expected Version': function(test) {
test.expect(1); 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, Long.fromNumber(10), event)
.then(function(result) { .then(function(result) {
test.fail("Append succeeded but should have failed."); test.fail("Append succeeded but should have failed.");
test.done(); test.done();
@ -88,10 +92,10 @@ module.exports = {
'Append To Stream Deleted': function(test) { 'Append To Stream Deleted': function(test) {
test.expect(1); test.expect(1);
var self = this; var self = this;
this.conn.deleteStream(this.testStreamName, client.expectedVersion.noStream, true) this.conn.deleteStream(this.testStreamName, NOSTREAM_VERSION, true)
.then(function() { .then(function() {
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');
return self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, event) return self.conn.appendToStream(self.testStreamName, ANY_VERSION, event)
}) })
.then(function(result) { .then(function(result) {
test.fail("Append succeeded but should have failed."); test.fail("Append succeeded but should have failed.");
@ -108,10 +112,10 @@ module.exports = {
test.expect(1); 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, NOSTREAM_VERSION, metadata)
.then(function() { .then(function() {
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');
return self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, event) return self.conn.appendToStream(self.testStreamName, ANY_VERSION, event)
}) })
.then(function(result) { .then(function(result) {
test.fail("Append succeeded but should have failed."); test.fail("Append succeeded but should have failed.");

View File

@ -1,4 +1,4 @@
var client = require('../../src/client'); var client = require('../../lib/dist');
var uuid = require('uuid'); var uuid = require('uuid');
var settings = { var settings = {

View File

@ -1,4 +1,4 @@
var client = require('../../src/client'); var client = require('../../lib/dist');
var uuid = require('uuid'); var uuid = require('uuid');
var settings = { var settings = {

View File

@ -1,42 +1,114 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid'); var uuid = require('uuid');
var client = require('../../src/client'); var Long = require('long');
var client = require('../../lib/dist');
var FileLogger = require('../../src/common/log/fileLogger'); var FileLogger = require('../../src/common/log/fileLogger');
var NoopLogger = require('../../src/common/log/noopLogger'); var NoopLogger = require('../../src/common/log/noopLogger');
// Make sure we mess with protobufjs setup for bug #91
var protobufJS = require('protobufjs');
protobufJS.util.Long = undefined;
protobufJS.configure();
var settings = { var settings = {
log: new NoopLogger() log: new NoopLogger(),
useSslConnection: true,
validateServer: false
}; };
if (process.env.TESTS_VERBOSE_LOGGING === '1') { if (process.env.TESTS_VERBOSE_LOGGING === '1') {
settings.verboseLogging = true; settings.verboseLogging = true;
settings.log = new FileLogger('test-verbose.log'); settings.log = new FileLogger('test-verbose.log');
} }
var tcpEndPoint = {host: 'localhost', port: 1113}; function setUpWithGossipSeeds(cb) {
var gossipSeeds = [
function setUp(cb) { new client.GossipSeed({ host: process.env.EVENTSTORE_HOST_1 || '192.168.33.10', port: 2113 }),
new client.GossipSeed({ host: process.env.EVENTSTORE_HOST_2 || '192.168.33.11', port: 2113 }),
new client.GossipSeed({ host: process.env.EVENTSTORE_HOST_3 || '192.168.33.12', port: 2113 }),
];
this.log = settings.log; this.log = settings.log;
this.testStreamName = 'test-' + uuid.v4(); this.testStreamName = 'test-' + uuid.v4();
var connected = false; var connected = false;
this.conn = client.EventStoreConnection.create(settings, tcpEndPoint); this.conn = client.createConnection(settings, gossipSeeds);
this.conn.connect() this.conn
.then(function () { .connect()
//Doesn't mean anything, connection is just initiated .then(function () {
settings.log.debug("Connection to %j initialized.", tcpEndPoint); //Doesn't mean anything, connection is just initiated
}) settings.log.debug('Connection to %j initialized.', gossipSeeds);
.catch(function (err) { })
settings.log.error(err, "Initializing connection to %j failed.", tcpEndPoint); .catch(function (err) {
cb(err); settings.log.error(err, 'Initializing connection to %j failed.', gossipSeeds);
}); cb(err);
});
this.conn.on('closed', function (reason) { this.conn.on('closed', function (reason) {
if (connected) return; if (connected) return;
var error = new Error("Connection failed: " + reason); var error = new Error('Connection failed: ' + reason);
settings.log.error(error, "Connection to %j failed.", tcpEndPoint); settings.log.error(error, 'Connection to %j failed.', gossipSeeds);
cb(error); cb(error);
}); });
this.conn.on('connected', function (tcpEndPoint) { this.conn.on('connected', function (tcpEndPoint) {
if (connected) return; if (connected) return;
settings.log.debug("Connected to %j.", tcpEndPoint); settings.log.debug('Connected to %j.', tcpEndPoint);
connected = true;
cb();
});
}
function setUpWithDns(cb) {
var clusterDns = 'discover://' + process.env.EVENTSTORE_HOST + ':2113';
this.log = settings.log;
this.testStreamName = 'test-' + uuid.v4();
var connected = false;
this.conn = client.createConnection(settings, clusterDns);
this.conn
.connect()
.then(function () {
//Doesn't mean anything, connection is just initiated
settings.log.debug('Connection to %j initialized.', clusterDns);
})
.catch(function (err) {
settings.log.error(err, 'Initializing connection to %j failed.', clusterDns);
cb(err);
});
this.conn.on('closed', function (reason) {
if (connected) return;
var error = new Error('Connection failed: ' + reason);
settings.log.error(error, 'Connection to %j failed.', clusterDns);
cb(error);
});
this.conn.on('connected', function (tcpEndPoint) {
if (connected) return;
settings.log.debug('Connected to %j.', tcpEndPoint);
connected = true;
cb();
});
}
function setUpWithTcpEndpoint(cb) {
var tcpEndPoint = { host: process.env.EVENTSTORE_HOST || 'localhost', port: 1113 };
this.log = settings.log;
this.testStreamName = 'test-' + uuid.v4();
var connected = false;
this.conn = client.EventStoreConnection.create(settings, tcpEndPoint);
this.conn
.connect()
.then(function () {
//Doesn't mean anything, connection is just initiated
settings.log.debug('Connection to %j initialized.', tcpEndPoint);
})
.catch(function (err) {
settings.log.error(err, 'Initializing connection to %j failed.', tcpEndPoint);
cb(err);
});
this.conn.on('closed', function (reason) {
if (connected) return;
var error = new Error('Connection failed: ' + reason);
settings.log.error(error, 'Connection to %j failed.', tcpEndPoint);
cb(error);
});
this.conn.on('connected', function (tcpEndPoint) {
if (connected) return;
settings.log.debug('Connected to %j.', tcpEndPoint);
connected = true; connected = true;
cb(); cb();
}); });
@ -44,7 +116,7 @@ function setUp(cb) {
function tearDown(cb) { function tearDown(cb) {
this.conn.close(); this.conn.close();
this.conn.on('closed', function() { this.conn.on('closed', function () {
cb(); cb();
}); });
this.conn = null; this.conn = null;
@ -52,9 +124,8 @@ function tearDown(cb) {
function areEqual(name, actual, expected) { function areEqual(name, actual, expected) {
if (typeof expected !== 'object' || expected === null) if (typeof expected !== 'object' || expected === null)
this.strictEqual(actual, expected, util.format("Failed %s === %s, got %s.", name, expected, actual)); this.strictEqual(actual, expected, util.format('Failed %s === %s, got %s.', name, expected, actual));
else else this.deepEqual(actual, expected, util.format('Failed %s deepEqual %j, got %j.', name, expected, actual));
this.deepEqual(actual, expected, util.format("Failed %s deepEqual %j, got %j.", name, expected, actual));
} }
function fail(reason) { function fail(reason) {
@ -63,49 +134,75 @@ function fail(reason) {
function eventEqualEventData(name, resolvedEvent, eventData) { function eventEqualEventData(name, resolvedEvent, eventData) {
var ev = resolvedEvent.originalEvent; var ev = resolvedEvent.originalEvent;
this.ok(ev !== null, util.format("Failed %s !== null.", name + ".originalEvent")); this.ok(ev !== null, util.format('Failed %s !== null.', name + '.originalEvent'));
if (ev === null) return; if (ev === null) return;
this.areEqual(name + ".originalEvent.eventId", ev.eventId, eventData.eventId); this.areEqual(name + '.originalEvent.eventId', ev.eventId, eventData.eventId);
this.areEqual(name + ".originalEvent.eventType", ev.eventType, eventData.type); this.areEqual(name + '.originalEvent.eventType', ev.eventType, eventData.type);
this.ok(Buffer.compare(ev.data, eventData.data) === 0, name + ".originalEvent.data is not equal to original data."); this.ok(Buffer.compare(ev.data, eventData.data) === 0, name + '.originalEvent.data is not equal to original data.');
this.ok(Buffer.compare(ev.metadata, eventData.metadata) === 0, name + ".originalEvent.metadata is not equal to original metadata."); this.ok(
Buffer.compare(ev.metadata, eventData.metadata) === 0,
name + '.originalEvent.metadata is not equal to original metadata.'
);
}
function testRecordedEvent(name, event) {
this.ok(Long.isLong(event.eventNumber), name + '.eventNumber is not a Long');
this.ok(event.created instanceof Date, name + '.created is not a Date');
this.ok(typeof event.createdEpoch === 'number', name + '.createdEpoch is not a number');
} }
function testLiveEvent(name, event, evNumber) { function testLiveEvent(name, event, evNumber) {
this.ok(event.event, name + ".event not defined (or null)"); this.ok(event.event, name + '.event not defined (or null)');
this.ok(event.originalEvent, name + ".originalEvent not defined (or null)"); this.ok(event.originalEvent, name + '.originalEvent not defined (or null)');
this.ok(event.isResolved === false, name + ".isResolved should be true"); this.ok(event.isResolved === false, name + '.isResolved should be true');
this.ok(event.originalPosition instanceof client.Position, name + ".originalPosition is not an instance of Position"); this.ok(event.originalPosition instanceof client.Position, name + '.originalPosition is not an instance of Position');
this.ok(event.originalStreamId, name + ".originalStreamId not defined (or null)"); this.ok(event.originalStreamId, name + '.originalStreamId not defined (or null)');
this.ok(Long.isLong(event.originalEventNumber), name + '.originalEventNumber is not a Long');
if (typeof evNumber === 'number') { if (typeof evNumber === 'number') {
this.ok(event.originalEventNumber === evNumber, name + '.originalEventNumber expected ' + evNumber + ' got ' + event.originalEventNumber); this.ok(
} else { event.originalEventNumber.toNumber() === evNumber,
this.ok(typeof event.originalEventNumber === 'number', name + ".originalEventNumber is not a number"); name + '.originalEventNumber expected ' + evNumber + ' got ' + event.originalEventNumber
);
} }
testRecordedEvent.call(this, name + '.event', event.event);
} }
function testReadEvent(name, event, evNumber) { function testReadEvent(name, event, evNumber) {
this.ok(event.event, name + ".event not defined (or null)"); this.ok(event.event, name + '.event not defined (or null)');
this.ok(event.originalEvent, name + ".originalEvent not defined (or null)"); this.ok(event.originalEvent, name + '.originalEvent not defined (or null)');
this.ok(event.isResolved === false, name + ".isResolved should be true"); this.ok(event.isResolved === false, name + '.isResolved should be true');
this.ok(event.originalPosition === null, name + ".originalPosition is not null"); this.ok(event.originalPosition === null, name + '.originalPosition is not null');
this.ok(event.originalStreamId, name + ".originalStreamId not defined (or null)"); this.ok(event.originalStreamId, name + '.originalStreamId not defined (or null)');
this.ok(Long.isLong(event.originalEventNumber), name + '.originalEventNumber is not a Long');
if (typeof evNumber === 'number') { if (typeof evNumber === 'number') {
this.ok(event.originalEventNumber === evNumber, name + '.originalEventNumber expected ' + evNumber + ' got ' + event.originalEventNumber); this.ok(
} else { event.originalEventNumber.toNumber() === evNumber,
this.ok(typeof event.originalEventNumber === 'number', name + ".originalEventNumber is not a number"); name + '.originalEventNumber expected ' + evNumber + ' got ' + event.originalEventNumber
);
} }
testRecordedEvent.call(this, name + '.event', event.event);
} }
var _ = { var _ = {
'setUp': setUp, tearDown: tearDown,
'tearDown': tearDown
}; };
switch (process.env.EVENTSTORE_CONNECTION_TYPE) {
case 'gossip':
_.setUp = setUpWithGossipSeeds;
break;
case 'dns':
_.setUp = setUpWithDns;
break;
case 'tcp':
default:
_.setUp = setUpWithTcpEndpoint;
}
function wrap(name, testFunc) { function wrap(name, testFunc) {
var base = _[name]; var base = _[name];
if (base === undefined) { if (base === undefined) {
return function(test) { return function (test) {
settings.log.debug('--- %s ---', name); settings.log.debug('--- %s ---', name);
test.areEqual = areEqual.bind(test); test.areEqual = areEqual.bind(test);
test.fail = fail.bind(test); test.fail = fail.bind(test);
@ -113,36 +210,48 @@ function wrap(name, testFunc) {
test.testLiveEvent = testLiveEvent.bind(test); test.testLiveEvent = testLiveEvent.bind(test);
test.testReadEvent = testReadEvent.bind(test); test.testReadEvent = testReadEvent.bind(test);
return testFunc.call(this, test); return testFunc.call(this, test);
} };
} }
return function(cb) { return function (cb) {
var self = this; var self = this;
base.call(this, function(err) { base.call(this, function (err) {
if (err) return cb(err); if (err) return cb(err);
return testFunc.call(self, cb); return testFunc.call(self, cb);
}); });
} };
} }
module.exports.init = function(testSuite, addSetUpTearDownIfNotPresent) { module.exports.init = function (testSuite, addSetUpTearDownIfNotPresent) {
var thisObj = {}; var thisObj = {};
if (addSetUpTearDownIfNotPresent === undefined) addSetUpTearDownIfNotPresent = true; if (addSetUpTearDownIfNotPresent === undefined) addSetUpTearDownIfNotPresent = true;
for(var k in testSuite) { for (var k in testSuite) {
if (testSuite.hasOwnProperty(k)) { if (testSuite.hasOwnProperty(k)) {
testSuite[k] = wrap(k, testSuite[k]).bind(thisObj); testSuite[k] = wrap(k, testSuite[k]).bind(thisObj);
} }
} }
if (!addSetUpTearDownIfNotPresent) return; if (!addSetUpTearDownIfNotPresent) return;
if (!testSuite.hasOwnProperty('setUp')) testSuite['setUp'] = setUp.bind(thisObj); if (!testSuite.hasOwnProperty('setUp')) {
switch (process.env.EVENTSTORE_CONNECTION_TYPE) {
case 'gossip':
testSuite['setUp'] = setUpWithGossipSeeds.bind(thisObj);
break;
case 'dns':
testSuite['setUp'] = setUpWithDns.bind(thisObj);
break;
case 'tcp':
default:
testSuite['setUp'] = setUpWithTcpEndpoint.bind(thisObj);
}
}
if (!testSuite.hasOwnProperty('tearDown')) testSuite['tearDown'] = tearDown.bind(thisObj); if (!testSuite.hasOwnProperty('tearDown')) testSuite['tearDown'] = tearDown.bind(thisObj);
}; };
module.exports.settings = function(settingsOverride) { module.exports.settings = function (settingsOverride) {
var obj = {}; var obj = {};
for(var prop in settings) { for (var prop in settings) {
obj[prop] = settings[prop]; obj[prop] = settings[prop];
} }
if (!settingsOverride) return obj; if (!settingsOverride) return obj;
for(var prop in settingsOverride) { for (var prop in settingsOverride) {
obj[prop] = settingsOverride[prop]; obj[prop] = settingsOverride[prop];
} }
return obj; return obj;

View File

@ -1,78 +1,176 @@
var client = require('../src/client.js'); var client = require('../lib/dist');
var GossipSeed = require('../src/gossipSeed'); var GossipSeed = require('../src/gossipSeed');
var testBase = require('./common/base_test'); var testBase = require('./common/base_test');
module.exports = { var withSsl = !!process.env.NODE_ESC_WITH_SSL;
'Connect To Endpoint Happy Path': function(test) { const evenstStoreType = process.env.EVENTSTORE_CONNECTION_TYPE;
test.expect(1);
var tcpEndpoint = {host: 'localhost', port: 1113}; module.exports = {}
var conn = client.EventStoreConnection.create(testBase.settings(), tcpEndpoint);
conn.connect() switch(evenstStoreType){
case 'gossip':
module.exports['Connect to Cluster using gossip seeds'] = function (test) {
test.expect(1);
var gossipSeeds = [
new GossipSeed({host: process.env.EVENTSTORE_HOST_1 || '192.168.33.10', port: 2113}),
new GossipSeed({host: process.env.EVENTSTORE_HOST_2 || '192.168.33.11', port: 2113}),
new GossipSeed({host: process.env.EVENTSTORE_HOST_3 || '192.168.33.12', port: 2113})
];
var conn = client.EventStoreConnection.create(testBase.settings(), gossipSeeds);
conn.connect()
.catch(function(err) { .catch(function(err) {
test.done(err); test.done(err);
}); });
conn.on('connected', function(endPoint){ conn.on('connected', function(endPoint){
test.areEqual("connected endPoint", endPoint, tcpEndpoint); test.ok(endPoint, 'no endpoint');
done(); done();
}); });
conn.on('error', done); conn.on('error', done);
function done(err) { function done(err) {
conn.close(); conn.close();
if (err) return test.done(err); if (err) return test.done(err);
test.done(); test.done();
} }
}, };
'Connect To Endpoint That Doesn\'t Exist': function(test) {
test.expect(1); module.exports['Connect To Cluster with bad gossip seeds'] = function (test) {
var tcpEndpoint = {host: 'localhost', port: 11112}; test.expect(3);
var conn = client.EventStoreConnection.create(testBase.settings({maxReconnections:1}), tcpEndpoint); var gossipSeeds = [
conn.connect() new GossipSeed({host: '1.2.3.4', port: 1113}),
new GossipSeed({host: '2.3.4.5', port: 2113}),
new GossipSeed({host: '3.4.5.6', port: 3113})
];
var conn = client.EventStoreConnection.create(testBase.settings({maxDiscoverAttempts: 1}), gossipSeeds);
conn.connect()
.catch(function (err) {
test.ok(err.message.indexOf('Couldn\'t resolve target end point') === 0, 'Wrong expected reason.');
});
conn.on('connected', function () {
test.ok(false, 'Should not be able to connect.');
});
conn.on('error', function (err) {
test.ok(err.message.indexOf('Failed to discover candidate in 1 attempts') === 0, 'Wrong expected reason.');
});
conn.on('closed', function (reason) {
test.ok(reason.indexOf('Failed to resolve TCP end point to which to connect') === 0, 'Wrong expected reason.');
test.done();
});
};
break;
case 'dns':
module.exports['Connect to Cluster using dns discover'] = function (test) {
test.expect(1);
var clusterDns = 'discover://' + process.env.EVENTSTORE_HOST + ':2113';
var conn = client.EventStoreConnection.create(testBase.settings(), clusterDns);
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();
}
};
module.exports['Connect To Cluster with bad dns discover'] = function (test) {
test.expect(3);
var clusterDns = 'discover://abc.def.com:2113';
var conn = client.EventStoreConnection.create(testBase.settings({maxDiscoverAttempts: 1}), clusterDns);
conn.connect()
.catch(function (err) {
test.ok(err.message.indexOf('Couldn\'t resolve target end point') === 0, 'Wrong expected reason.');
});
conn.on('connected', function () {
test.ok(false, 'Should not be able to connect.');
conn.close();
});
conn.on('error', function (err) {
test.ok(err.message.indexOf('Failed to discover candidate in 1 attempts') === 0, 'Wrong expected reason.');
});
conn.on('closed', function (reason) {
test.ok(reason.indexOf('Failed to resolve TCP end point to which to connect') === 0, 'Wrong expected reason.');
test.done();
});
};
break;
case 'tcp':
default:
module.exports['Connect To Endpoint Happy Path'] = function (test) {
test.expect(1);
var tcpEndpoint = {host: process.env.EVENTSTORE_HOST || 'localhost', port: 1113};
var conn = client.EventStoreConnection.create(testBase.settings(), tcpEndpoint);
conn.connect()
.catch(function (err) { .catch(function (err) {
test.done(err); test.done(err);
}); });
conn.on('connected', function () { conn.on('connected', function (endPoint) {
test.ok(false, "Should not be able to connect."); test.areEqual('connected endPoint', endPoint, tcpEndpoint);
done();
});
conn.on('error', done);
function done(err) {
conn.close();
if (err) return test.done(err);
test.done();
}
};
module.exports['Connect To Endpoint That Doesn\'t Exist'] = function (test) {
test.expect(1);
var tcpEndpoint = {host: process.env.EVENTSTORE_HOST || 'localhost', port: 11112};
var conn = client.EventStoreConnection.create(testBase.settings({maxReconnections: 1}), tcpEndpoint);
conn.connect()
.catch(function (err) {
test.done(err);
});
conn.on('connected', function () {
test.ok(false, 'Should not be able to connect.');
test.done();
});
conn.on('error', function (err) {
test.done(err);
});
conn.on('closed', function (reason) {
test.ok(reason.indexOf('Reconnection limit reached') === 0, 'Wrong expected reason.');
test.done();
});
};
module.exports['Create a connection with tcp://host:port string'] = function (test) {
var conn = client.createConnection({}, `tcp://${process.env.EVENTSTORE_HOST || 'localhost'}:1113`);
conn.close();
test.done(); test.done();
}); };
}
if (withSsl) {
module.exports['Connect to secure tcp endpoint'] = function(test) {
var conn = client.createConnection({
useSslConnection: true,
targetHost: process.env.EVENTSTORE_HOST || 'localhost',
validateServer: false
}, `tcp://${process.env.EVENTSTORE_HOST || 'localhost'}:1115`);
conn.on('error', function (err) { conn.on('error', function (err) {
test.done(err); test.done(err);
}); });
conn.on('closed', function(reason) {
test.ok(reason.indexOf("Reconnection limit reached") === 0, "Wrong expected reason.");
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() conn.connect()
.catch(function(err) { .catch(function (err) {
test.done(err); test.done(err);
}); });
conn.on('connected', function(endPoint){ conn.on('connected', function () {
test.ok(endPoint, "no endpoint");
done();
});
conn.on('error', done);
function done(err) {
conn.close();
if (err) return test.done(err);
test.done(); test.done();
} });
}*/ }
}; }
testBase.init(module.exports, false); testBase.init(module.exports, false);

View File

@ -1,5 +1,6 @@
var uuid = require('uuid'); var uuid = require('uuid');
var client = require('../src/client'); var client = require('../lib/dist');
var Long = require('long');
module.exports = { module.exports = {
setUp: function(cb) { setUp: function(cb) {
@ -16,7 +17,7 @@ module.exports = {
'Test Delete Stream Soft Happy Path': function(test) { 'Test Delete Stream Soft Happy Path': function(test) {
test.expect(4); test.expect(4);
var self = this; var self = this;
this.conn.deleteStream(this.testStreamName, 1, false) this.conn.deleteStream(this.testStreamName, Long.fromNumber(1), false)
.then(function(result) { .then(function(result) {
test.ok(result.logPosition, "No log position in result."); test.ok(result.logPosition, "No log position in result.");
return self.conn.getStreamMetadataRaw(self.testStreamName); return self.conn.getStreamMetadataRaw(self.testStreamName);
@ -34,7 +35,7 @@ module.exports = {
'Test Delete Stream Hard Happy Path': function(test) { 'Test Delete Stream Hard Happy Path': function(test) {
test.expect(4); test.expect(4);
var self = this; var self = this;
this.conn.deleteStream(this.testStreamName, 1, true) this.conn.deleteStream(this.testStreamName, Long.fromNumber(1), true)
.then(function(result) { .then(function(result) {
test.ok(result.logPosition, "No log position in result."); test.ok(result.logPosition, "No log position in result.");
return self.conn.getStreamMetadataRaw(self.testStreamName); return self.conn.getStreamMetadataRaw(self.testStreamName);
@ -51,7 +52,7 @@ module.exports = {
}, },
'Test Delete Stream With Wrong Expected Version': function(test) { 'Test Delete Stream With Wrong Expected Version': function(test) {
test.expect(1); test.expect(1);
this.conn.deleteStream(this.testStreamName, 10) this.conn.deleteStream(this.testStreamName, Long.fromNumber(10))
.then(function(result) { .then(function(result) {
test.fail("Delete succeeded but should have failed."); test.fail("Delete succeeded but should have failed.");
test.done(); test.done();
@ -68,7 +69,7 @@ module.exports = {
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() {
return self.conn.deleteStream(self.testStreamName, 10); return self.conn.deleteStream(self.testStreamName, Long.fromNumber(10));
}) })
.then(function(result) { .then(function(result) {
test.fail("Delete succeeded but should have failed."); test.fail("Delete succeeded but should have failed.");
@ -86,7 +87,7 @@ module.exports = {
var self = this; var self = this;
this.conn.deleteStream(this.testStreamName, 1, true) this.conn.deleteStream(this.testStreamName, 1, true)
.then(function() { .then(function() {
return self.conn.deleteStream(self.testStreamName, 1, true); return self.conn.deleteStream(self.testStreamName, Long.fromNumber(1), true);
}) })
.then(function(result) { .then(function(result) {
test.fail("Delete succeeded but should have failed."); test.fail("Delete succeeded but should have failed.");

98
test/fixtures/gossip.json vendored Normal file
View File

@ -0,0 +1,98 @@
{
"members": [
{
"instanceId": "bb16857d-373d-4233-a175-89c917a72329",
"timeStamp": "2020-09-02T13:53:24.234898Z",
"state": "Slave",
"isAlive": false,
"internalTcpIp": "10.0.0.1",
"internalTcpPort": 1112,
"internalSecureTcpPort": 0,
"externalTcpIp": "10.0.0.1",
"externalTcpPort": 1113,
"externalSecureTcpPort": 0,
"internalHttpIp": "10.0.0.1",
"internalHttpPort": 2112,
"externalHttpIp": "10.0.0.1",
"externalHttpPort": 2113,
"lastCommitPosition": 648923382,
"writerCheckpoint": 648936339,
"chaserCheckpoint": 648936339,
"epochPosition": 551088596,
"epochNumber": 201,
"epochId": "d8f95f4b-167a-4487-9031-4d31a507e6d9",
"nodePriority": 0
},
{
"instanceId": "b3c18dcd-6476-467a-b7b8-d6672b74e9c2",
"timeStamp": "2020-09-02T13:56:06.189428Z",
"state": "CatchingUp",
"isAlive": true,
"internalTcpIp": "10.0.0.2",
"internalTcpPort": 1112,
"internalSecureTcpPort": 0,
"externalTcpIp": "10.0.0.2",
"externalTcpPort": 1113,
"externalSecureTcpPort": 0,
"internalHttpIp": "10.0.0.2",
"internalHttpPort": 2112,
"externalHttpIp": "10.0.0.2",
"externalHttpPort": 2113,
"lastCommitPosition": -1,
"writerCheckpoint": 0,
"chaserCheckpoint": 0,
"epochPosition": -1,
"epochNumber": -1,
"epochId": "00000000-0000-0000-0000-000000000000",
"nodePriority": 0
},
{
"instanceId": "e802a2b5-826c-4bd5-84d0-c9d1387fbf79",
"timeStamp": "2020-09-02T13:56:07.391534Z",
"state": "Master",
"isAlive": true,
"internalTcpIp": "10.0.0.3",
"internalTcpPort": 1112,
"internalSecureTcpPort": 0,
"externalTcpIp": "10.0.0.3",
"externalTcpPort": 1113,
"externalSecureTcpPort": 0,
"internalHttpIp": "10.0.0.3",
"internalHttpPort": 2112,
"externalHttpIp": "10.0.0.3",
"externalHttpPort": 2113,
"lastCommitPosition": 649007631,
"writerCheckpoint": 649024685,
"chaserCheckpoint": 649024685,
"epochPosition": 649023795,
"epochNumber": 202,
"epochId": "1f17695d-6558-4d8b-ba60-2ae273b11e09",
"nodePriority": 0
},
{
"instanceId": "24bb9031-5f21-436c-a7b5-c5f03a95e938",
"timeStamp": "2020-09-02T13:54:39.023053Z",
"state": "Slave",
"isAlive": false,
"internalTcpIp": "10.0.0.4",
"internalTcpPort": 1112,
"internalSecureTcpPort": 0,
"externalTcpIp": "10.0.0.4",
"externalTcpPort": 1113,
"externalSecureTcpPort": 0,
"internalHttpIp": "10.0.0.4",
"internalHttpPort": 2112,
"externalHttpIp": "10.0.0.4",
"externalHttpPort": 2113,
"lastCommitPosition": 649007631,
"writerCheckpoint": 649023795,
"chaserCheckpoint": 649023795,
"epochPosition": 551088596,
"epochNumber": 201,
"epochId": "d8f95f4b-167a-4487-9031-4d31a507e6d9",
"nodePriority": 0
}
],
"serverIp": "10.0.0.3",
"serverPort": 2112
}

View File

@ -1,10 +1,11 @@
var util = require('util'); var client = require('../lib/dist');
var uuid = require('uuid'); const Long = require('long');
var client = require('../src/client');
const EMPTY_VERSION = Long.fromNumber(client.expectedVersion.emptyStream);
module.exports = { module.exports = {
'Test Set Stream Metadata Raw': function(test) { 'Test Set Stream Metadata Raw': function(test) {
this.conn.setStreamMetadataRaw(this.testStreamName, client.expectedVersion.emptyStream, {$maxCount: 100}) this.conn.setStreamMetadataRaw(this.testStreamName, EMPTY_VERSION, {$maxCount: 100})
.then(function(result) { .then(function(result) {
test.done(); test.done();
}) })

View File

@ -1,6 +1,6 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid'); var uuid = require('uuid');
var client = require('../src/client'); var client = require('../lib/dist');
const adminCredentials = new client.UserCredentials("admin", "changeit"); const adminCredentials = new client.UserCredentials("admin", "changeit");
function createRandomEvent() { function createRandomEvent() {
@ -9,20 +9,6 @@ function createRandomEvent() {
var testStreamName = 'test-' + uuid.v4(); var testStreamName = 'test-' + uuid.v4();
function delay(ms) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, ms);
})
}
function delayOnlyFirst(count, action) {
if (count === 0) return action();
return delay(200)
.then(function () {
action();
})
}
module.exports = { module.exports = {
'Test Create Persistent Subscription': function(test) { 'Test Create Persistent Subscription': function(test) {
var settings = client.PersistentSubscriptionSettings.create(); var settings = client.PersistentSubscriptionSettings.create();
@ -46,10 +32,8 @@ module.exports = {
test.done(); test.done();
} }
function eventAppeared(s, e) { function eventAppeared(s, e) {
return delayOnlyFirst(receivedEvents.length, function () { receivedEvents.push(e);
receivedEvents.push(e); if (receivedEvents.length === 2) s.stop();
if (receivedEvents.length === 2) s.stop();
});
} }
function subscriptionDropped(connection, reason, error) { function subscriptionDropped(connection, reason, error) {
if (error) return done(error); if (error) return done(error);
@ -57,7 +41,7 @@ module.exports = {
done(); done();
} }
var self = this; var self = this;
this.conn.connectToPersistentSubscription(testStreamName, 'consumer-1', eventAppeared, subscriptionDropped) this.conn.connectToPersistentSubscription(testStreamName, 'consumer-1', eventAppeared, subscriptionDropped, adminCredentials)
.then(function(subscription) { .then(function(subscription) {
test.ok(subscription, "Subscription is null."); test.ok(subscription, "Subscription is null.");
return self.conn.appendToStream(testStreamName, client.expectedVersion.any, [createRandomEvent(), createRandomEvent()]); return self.conn.appendToStream(testStreamName, client.expectedVersion.any, [createRandomEvent(), createRandomEvent()]);

View File

@ -1,8 +1,8 @@
const client = require('../src/client'); const client = require('../lib/dist');
const userCredentials = new client.UserCredentials('admin', 'changeit'); const userCredentials = new client.UserCredentials('admin', 'changeit');
const log = new client.NoopLogger(); const log = new client.NoopLogger();
const httpEndpoint = 'http://127.0.0.1:2113'; const httpEndpoint = `https://${process.env.EVENTSTORE_HOST || "localhost"}:2113`;
const operationTimeout = 5000; const operationTimeout = 5000;
const simpleProjection = "\ const simpleProjection = "\
@ -21,7 +21,7 @@ fromStream('$stats-127.0.0.1:2113')\
module.exports = { module.exports = {
setUp: function(cb) { setUp: function(cb) {
this.projectionsManager = new client.ProjectionsManager(log, httpEndpoint, operationTimeout); this.projectionsManager = new client.ProjectionsManager(log, httpEndpoint, operationTimeout, false);
cb(); cb();
}, },
'Create One Time Projection Happy Path': function(test) { 'Create One Time Projection Happy Path': function(test) {

View File

@ -1,6 +1,6 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid'); var uuid = require('uuid');
var client = require('../src/client'); var client = require('../lib/dist');
const numberOfStreams = 20; const numberOfStreams = 20;
const maxBatch = 10; const maxBatch = 10;

View File

@ -1,6 +1,6 @@
var util = require('util'); var util = require('util');
var uuid = require('uuid'); var uuid = require('uuid');
var client = require('../src/client'); var client = require('../lib/dist');
const numberOfStreams = 20; const numberOfStreams = 20;
const maxBatch = 10; const maxBatch = 10;

View File

@ -1,7 +1,9 @@
var util = require('util');
var uuid = require('uuid'); var uuid = require('uuid');
var client = require('../src/client'); var client = require('../lib/dist');
var Long = require('long');
var NOSTREAM_VERSION = Long.fromNumber(client.expectedVersion.noStream);
var ANY_VERSION = Long.fromNumber(client.expectedVersion.any);
module.exports = { module.exports = {
setUp: function(cb) { setUp: function(cb) {
@ -12,7 +14,7 @@ module.exports = {
this.expectedEventType = 'anEvent'; this.expectedEventType = 'anEvent';
this.expectedEventId = uuid.v4(); this.expectedEventId = uuid.v4();
var event = client.createJsonEventData(this.expectedEventId, this.expectedEvent, null, this.expectedEventType); var event = client.createJsonEventData(this.expectedEventId, this.expectedEvent, null, this.expectedEventType);
this.conn.appendToStream(this.testStreamName, client.expectedVersion.noStream, event) this.conn.appendToStream(this.testStreamName, NOSTREAM_VERSION, event)
.then(function() { .then(function() {
cb(); cb();
}) })
@ -21,11 +23,11 @@ module.exports = {
'Read Event Happy Path': function(test) { 'Read Event Happy Path': function(test) {
test.expect(8); test.expect(8);
var self = this; var self = this;
this.conn.readEvent(this.testStreamName, 0) this.conn.readEvent(this.testStreamName, Long.fromNumber(0))
.then(function(result) { .then(function(result) {
test.areEqual('status', result.status, client.eventReadStatus.Success); test.areEqual('status', result.status, client.eventReadStatus.Success);
test.areEqual('stream', result.stream, self.testStreamName); test.areEqual('stream', result.stream, self.testStreamName);
test.areEqual('eventNumber', result.eventNumber, 0); test.areEqual('eventNumber', result.eventNumber, Long.fromNumber(0));
test.ok(result.event !== null, "event is null."); test.ok(result.event !== null, "event is null.");
test.ok(result.event.originalEvent !== null, "event.originalEvent is null."); test.ok(result.event.originalEvent !== null, "event.originalEvent is null.");
var event = JSON.parse(result.event.originalEvent.data.toString()); var event = JSON.parse(result.event.originalEvent.data.toString());
@ -41,11 +43,11 @@ module.exports = {
'Read Event From Non-Existing Stream': function(test) { 'Read Event From Non-Existing Stream': function(test) {
test.expect(4); test.expect(4);
var anotherStream = 'test' + uuid.v4(); var anotherStream = 'test' + uuid.v4();
this.conn.readEvent(anotherStream, 0) this.conn.readEvent(anotherStream, Long.fromNumber(0))
.then(function(result) { .then(function(result) {
test.areEqual('status', result.status, client.eventReadStatus.NoStream); test.areEqual('status', result.status, client.eventReadStatus.NoStream);
test.areEqual('stream', result.stream, anotherStream); test.areEqual('stream', result.stream, anotherStream);
test.areEqual('eventNumber', result.eventNumber, 0); test.areEqual('eventNumber', result.eventNumber, Long.fromNumber(0));
test.areEqual('event', result.event, null); test.areEqual('event', result.event, null);
test.done(); test.done();
}) })
@ -58,12 +60,12 @@ module.exports = {
var self = this; var self = this;
this.conn.deleteStream(this.testStreamName, 0, true) this.conn.deleteStream(this.testStreamName, 0, true)
.then(function() { .then(function() {
return self.conn.readEvent(self.testStreamName, 0) return self.conn.readEvent(self.testStreamName, Long.fromNumber(0))
}) })
.then(function(result) { .then(function(result) {
test.areEqual('status', result.status, client.eventReadStatus.StreamDeleted); test.areEqual('status', result.status, client.eventReadStatus.StreamDeleted);
test.areEqual('stream', result.stream, self.testStreamName); test.areEqual('stream', result.stream, self.testStreamName);
test.areEqual('eventNumber', result.eventNumber, 0); test.areEqual('eventNumber', result.eventNumber, Long.fromNumber(0));
test.areEqual('event', result.event, null); test.areEqual('event', result.event, null);
test.done(); test.done();
}) })
@ -74,11 +76,11 @@ module.exports = {
'Read Event With Inexisting Version': function(test) { 'Read Event With Inexisting Version': function(test) {
test.expect(4); test.expect(4);
var self = this; var self = this;
return self.conn.readEvent(self.testStreamName, 1) return self.conn.readEvent(self.testStreamName, Long.fromNumber(1))
.then(function(result) { .then(function(result) {
test.areEqual('status', result.status, client.eventReadStatus.NotFound); test.areEqual('status', result.status, client.eventReadStatus.NotFound);
test.areEqual('stream', result.stream, self.testStreamName); test.areEqual('stream', result.stream, self.testStreamName);
test.areEqual('eventNumber', result.eventNumber, 1); test.areEqual('eventNumber', result.eventNumber, Long.fromNumber(1));
test.areEqual('event', result.event, null); test.areEqual('event', result.event, null);
test.done(); test.done();
}) })
@ -94,9 +96,9 @@ module.exports = {
$r: '$admins' $r: '$admins'
} }
}; };
this.conn.setStreamMetadataRaw(self.testStreamName, client.expectedVersion.noStream, metadata) this.conn.setStreamMetadataRaw(self.testStreamName, NOSTREAM_VERSION, metadata)
.then(function() { .then(function() {
return self.conn.readEvent(self.testStreamName, 0); return self.conn.readEvent(self.testStreamName, Long.fromNumber(0));
}) })
.then(function(result) { .then(function(result) {
test.fail("readEvent succeeded but should have failed."); test.fail("readEvent succeeded but should have failed.");
@ -118,13 +120,13 @@ module.exports = {
a: largeData.toString() a: largeData.toString()
}, null, 'largePayloadEvent'); }, null, 'largePayloadEvent');
this.conn.appendToStream(this.testStreamName, client.expectedVersion.any, largeEvent) this.conn.appendToStream(this.testStreamName, ANY_VERSION, largeEvent)
.then(function(result) { .then(function(result) {
self.conn.readEvent(self.testStreamName, 1) self.conn.readEvent(self.testStreamName, Long.fromNumber(1))
.then(function(result) { .then(function(result) {
test.areEqual('status', result.status, client.eventReadStatus.Success); test.areEqual('status', result.status, client.eventReadStatus.Success);
test.areEqual('stream', result.stream, self.testStreamName); test.areEqual('stream', result.stream, self.testStreamName);
test.areEqual('eventNumber', result.eventNumber, 1); test.areEqual('eventNumber', result.eventNumber, Long.fromNumber(1));
test.ok(result.event !== null, "event is null."); test.ok(result.event !== null, "event is null.");
test.ok(result.event.originalEvent !== null, "event.originalEvent is null."); test.ok(result.event.originalEvent !== null, "event.originalEvent is null.");
var event = JSON.parse(result.event.originalEvent.data.toString()); var event = JSON.parse(result.event.originalEvent.data.toString());

Some files were not shown because too many files have changed in this diff Show More