tapeworm 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/event_store_partition.js +77 -33
- package/lib/event_stream.js +122 -80
- package/lib/persistence/inmemory/inmemory_partition.js +22 -11
- package/package.json +7 -4
- package/test/event_partition.js +181 -100
- package/test/event_stream.js +53 -2
- package/test/persistence/inmemory.js +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
var _ = require('lodash');
|
|
2
2
|
var Promise = require('bluebird');
|
|
3
|
+
var uuid = require('uuid').v4;
|
|
3
4
|
|
|
4
5
|
var EventStream = require('./event_stream');
|
|
5
6
|
|
|
@@ -9,40 +10,47 @@ var EventStorePartition = function (partitionId, persistencePartition, dispatchS
|
|
|
9
10
|
this._dispatchService = dispatchService;
|
|
10
11
|
|
|
11
12
|
var self = this;
|
|
12
|
-
_.forEach(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
17
|
-
} else {
|
|
18
|
-
if (what === 'queryStreamWithSnapshot') {
|
|
19
|
-
// fallback function
|
|
13
|
+
_.forEach(
|
|
14
|
+
['storeSnapshot', 'loadSnapshot', 'queryStream', 'queryStreamWithSnapshot', 'removeSnapshot', 'getLatestCommit', 'querySnapshotsByMaxDateTime'],
|
|
15
|
+
function (what) {
|
|
16
|
+
if (self._persistencePartition[what]) {
|
|
20
17
|
self[what] = function () {
|
|
21
|
-
return self.
|
|
18
|
+
return self._persistencePartition[what].apply(self._persistencePartition, arguments);
|
|
22
19
|
};
|
|
20
|
+
} else {
|
|
21
|
+
if (what === 'queryStreamWithSnapshot') {
|
|
22
|
+
// fallback function
|
|
23
|
+
self[what] = function () {
|
|
24
|
+
return self._queryStreamWithSnapshotFallback.apply(self._persistencePartition, arguments);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
23
27
|
}
|
|
24
28
|
}
|
|
25
|
-
|
|
29
|
+
);
|
|
26
30
|
};
|
|
27
31
|
|
|
28
|
-
EventStorePartition.prototype.openStream = function(streamId, callback) {
|
|
29
|
-
|
|
32
|
+
EventStorePartition.prototype.openStream = function (streamId, writeOnly, callback) {
|
|
33
|
+
if (typeof writeOnly === 'function') {
|
|
34
|
+
callback = writeOnly;
|
|
35
|
+
writeOnly = false;
|
|
36
|
+
}
|
|
37
|
+
var stream = new EventStream(this, streamId, writeOnly);
|
|
30
38
|
return stream._prepareStream(callback);
|
|
31
39
|
};
|
|
32
40
|
|
|
33
|
-
EventStorePartition.prototype.append = function(commits, callback) {
|
|
41
|
+
EventStorePartition.prototype.append = function (commits, callback) {
|
|
34
42
|
//pre hooks
|
|
35
43
|
var self = this;
|
|
36
44
|
|
|
37
|
-
if(!_.isArray(commits)) {
|
|
45
|
+
if (!_.isArray(commits)) {
|
|
38
46
|
commits = [commits];
|
|
39
47
|
}
|
|
40
|
-
return Promise.each(commits, function(commit) {
|
|
41
|
-
return self._persistencePartition.append(commit, callback).then(function(r) {
|
|
42
|
-
var done = function() {
|
|
48
|
+
return Promise.each(commits, function (commit) {
|
|
49
|
+
return self._persistencePartition.append(commit, callback).then(function (r) {
|
|
50
|
+
var done = function () {
|
|
43
51
|
self._persistencePartition.markAsDispatched(commit);
|
|
44
|
-
}
|
|
45
|
-
if(self._dispatchService) {
|
|
52
|
+
};
|
|
53
|
+
if (self._dispatchService) {
|
|
46
54
|
self._dispatchService(commit, done);
|
|
47
55
|
}
|
|
48
56
|
return r;
|
|
@@ -50,46 +58,82 @@ EventStorePartition.prototype.append = function(commits, callback) {
|
|
|
50
58
|
});
|
|
51
59
|
//post hooks
|
|
52
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* Delete function in tapeworm,
|
|
63
|
+
* Delete should trigger a $stream.deleted.event, including aggregateType of first event and publish it.
|
|
64
|
+
* 2. Implement in projector service to listen to this event and then do a delete of all related projections.,
|
|
65
|
+
* 3. Delete function should store the following new commit (deleted event) under the stream:
|
|
66
|
+
* streamId,
|
|
67
|
+
* aggregateType,
|
|
68
|
+
* date of deletion,
|
|
69
|
+
* date of creation,
|
|
70
|
+
* principal
|
|
71
|
+
* 4. Then remove snapshot for this stream and all commits for this stream.
|
|
72
|
+
* @param {*} streamId
|
|
73
|
+
* @param {*} deleteEvent optional delete event
|
|
74
|
+
*/
|
|
75
|
+
EventStorePartition.prototype.delete = function (streamId, deleteEvent) {
|
|
76
|
+
var self = this;
|
|
77
|
+
var event = Object.assign({}, deleteEvent, {
|
|
78
|
+
type: '$stream.deleted.event',
|
|
79
|
+
payload: Object.assign({}, { dateOfDeletion: new Date() }, (deleteEvent && deleteEvent.payload) || {})
|
|
80
|
+
});
|
|
81
|
+
return this._truncateStreamFrom(streamId, -1)
|
|
82
|
+
.then(function (result) {
|
|
83
|
+
return self.openStream(streamId, false);
|
|
84
|
+
})
|
|
85
|
+
.then(function (stream) {
|
|
86
|
+
stream.append(event);
|
|
87
|
+
return stream.commit(uuid());
|
|
88
|
+
})
|
|
89
|
+
.then(function () {
|
|
90
|
+
// support legacy/unsupported approach
|
|
91
|
+
if (self._persistencePartition.removeSnapshot) {
|
|
92
|
+
return self._persistencePartition.removeSnapshot(streamId);
|
|
93
|
+
} else if (self._persistencePartition.removeSnapshots) {
|
|
94
|
+
return self._persistencePartition.removeSnapshots([streamId]);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
};
|
|
53
98
|
|
|
54
99
|
/*** UNDOCUMENTED API ***/
|
|
55
100
|
|
|
56
|
-
EventStorePartition.prototype._queryStream = function(streamId, callback) {
|
|
101
|
+
EventStorePartition.prototype._queryStream = function (streamId, callback) {
|
|
57
102
|
return this._persistencePartition.queryStream(streamId, callback);
|
|
58
103
|
};
|
|
59
104
|
|
|
60
|
-
EventStorePartition.prototype._queryAll = function(callback) {
|
|
105
|
+
EventStorePartition.prototype._queryAll = function (callback) {
|
|
61
106
|
return this._persistencePartition.queryAll(callback);
|
|
62
107
|
};
|
|
63
108
|
|
|
64
|
-
EventStorePartition.prototype._queryStreamWithSnapshotFallback = function(streamId, callback) {
|
|
109
|
+
EventStorePartition.prototype._queryStreamWithSnapshotFallback = function (streamId, callback) {
|
|
65
110
|
var self = this;
|
|
66
111
|
return new Promise(function (resolve, reject) {
|
|
67
112
|
self.loadSnapshot(streamId, function (err, snapshot) {
|
|
68
113
|
if (err) {
|
|
69
|
-
reject(err)
|
|
114
|
+
reject(err);
|
|
70
115
|
} else {
|
|
71
|
-
var snapshotVersion = snapshot && snapshot.version || -1;
|
|
116
|
+
var snapshotVersion = (snapshot && snapshot.version) || -1;
|
|
72
117
|
self.queryStream(streamId, snapshotVersion, function (err, res) {
|
|
73
|
-
resolve({ snapshot: snapshot, commits: res })
|
|
74
|
-
})
|
|
118
|
+
resolve({ snapshot: snapshot, commits: res });
|
|
119
|
+
});
|
|
75
120
|
}
|
|
76
|
-
})
|
|
121
|
+
});
|
|
77
122
|
}).nodeify(callback);
|
|
78
123
|
};
|
|
79
124
|
|
|
80
125
|
/*** NEEDED FOR SYNCING ***/
|
|
81
126
|
|
|
82
|
-
EventStorePartition.prototype._truncateStreamFrom = function(streamId, commitSequence, callback) {
|
|
127
|
+
EventStorePartition.prototype._truncateStreamFrom = function (streamId, commitSequence, callback) {
|
|
83
128
|
return this._persistencePartition.truncateStreamFrom(streamId, commitSequence, callback);
|
|
84
|
-
}
|
|
129
|
+
};
|
|
85
130
|
|
|
86
|
-
EventStorePartition.prototype._applyCommitHeader = function(commit, header, callback) {
|
|
131
|
+
EventStorePartition.prototype._applyCommitHeader = function (commit, header, callback) {
|
|
87
132
|
return this._persistencePartition.applyCommitHeader(commit, header, callback);
|
|
88
|
-
}
|
|
133
|
+
};
|
|
89
134
|
|
|
90
135
|
//EventStorePartition.prototype.loadEvents = function(streamId, callback)
|
|
91
136
|
|
|
92
|
-
|
|
93
137
|
/*EventStorePartition.prototype.append = function(streamId, expectedVersion, events) {
|
|
94
138
|
var commit = new Commit(uuid(), this._partitionId, streamId, expectedVersion, events);
|
|
95
139
|
return this._persistencePartition.append(commit).then(function()
|
|
@@ -101,4 +145,4 @@ EventStorePartition.prototype._applyCommitHeader = function(commit, header, call
|
|
|
101
145
|
};
|
|
102
146
|
*/
|
|
103
147
|
|
|
104
|
-
module.exports = EventStorePartition;
|
|
148
|
+
module.exports = EventStorePartition;
|
package/lib/event_stream.js
CHANGED
|
@@ -2,104 +2,146 @@ var Promise = require('bluebird');
|
|
|
2
2
|
var Commit = require('./persistence/commit');
|
|
3
3
|
var _ = require('lodash');
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
this._committedEvents = [];
|
|
19
|
-
this._commitSequence = -1;
|
|
20
|
-
var self = this;
|
|
21
|
-
|
|
22
|
-
return this._partition._queryStream(this._streamId, callback).then(function(commits) {
|
|
23
|
-
self._version = 0;
|
|
24
|
-
|
|
25
|
-
if(!commits || commits.length === 0) {
|
|
5
|
+
// writeOnly - will never read entire stream from
|
|
6
|
+
var EventStream = function (eventPartition, streamId, writeOnly) {
|
|
7
|
+
if (streamId === undefined) {
|
|
8
|
+
throw new Error('StreamId must be defined!');
|
|
9
|
+
}
|
|
10
|
+
this._partition = eventPartition;
|
|
11
|
+
this._streamId = streamId;
|
|
12
|
+
this._writeOnly = writeOnly;
|
|
13
|
+
this._uncommittedEvents = [];
|
|
14
|
+
this._committedEvents = [];
|
|
15
|
+
this._version = 0;
|
|
16
|
+
this._isDeleted = false;
|
|
17
|
+
};
|
|
26
18
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
19
|
+
EventStream.prototype._prepareStream = function (callback) {
|
|
20
|
+
this._committedEvents = [];
|
|
21
|
+
this._commitSequence = -1;
|
|
22
|
+
var self = this;
|
|
23
|
+
if (self._writeOnly === true && self._partition.getLatestCommit) {
|
|
24
|
+
// if stream should only be opened for appending commits, only really care about getting the correct commitSequence (from last commit)
|
|
25
|
+
return self._partition
|
|
26
|
+
.getLatestCommit(self._streamId)
|
|
27
|
+
.then(function (commit) {
|
|
28
|
+
if (commit) {
|
|
29
|
+
self._commitSequence = commit.commitSequence;
|
|
30
|
+
var lastEvent = commit.events[commit.events.length - 1];
|
|
31
|
+
if (lastEvent && lastEvent.type === '$stream.deleted.event') {
|
|
32
|
+
self._isDeleted = true;
|
|
33
|
+
}
|
|
34
|
+
self._version = lastEvent.version + 1;
|
|
35
|
+
}
|
|
36
|
+
// if no commit is found, assume its a new stream, and keep default versions
|
|
37
|
+
})
|
|
38
|
+
.then(function () {
|
|
39
|
+
return self;
|
|
40
|
+
});
|
|
41
|
+
} else {
|
|
42
|
+
return self._partition
|
|
43
|
+
._queryStream(self._streamId, callback)
|
|
44
|
+
.then(function (commits) {
|
|
45
|
+
self._version = 0;
|
|
46
|
+
|
|
47
|
+
if (!commits || commits.length === 0) {
|
|
48
|
+
commits = [];
|
|
49
|
+
self._version = -1;
|
|
50
|
+
} else {
|
|
51
|
+
if (commits[0] && commits[0].events && commits[0].events[0] && commits[0].events[0].type === '$stream.deleted.event') {
|
|
52
|
+
throw new Error('Stream is deleted');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
var version = 0;
|
|
56
|
+
for (var i = 0; i < commits.length; i++) {
|
|
57
|
+
self._commitSequence++;
|
|
58
|
+
for (var j = 0; j < commits[i].events.length; j++) {
|
|
59
|
+
self._version++;
|
|
60
|
+
commits[i].events[j].version = version++;
|
|
61
|
+
self._committedEvents.push(commits[i].events[j]);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
.then(function () {
|
|
66
|
+
return self;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
43
69
|
};
|
|
44
70
|
|
|
45
|
-
EventStream.prototype.getVersion = function() {
|
|
46
|
-
|
|
71
|
+
EventStream.prototype.getVersion = function () {
|
|
72
|
+
return this._version;
|
|
47
73
|
};
|
|
48
74
|
|
|
49
|
-
EventStream.prototype.append = function(event) {
|
|
50
|
-
|
|
75
|
+
EventStream.prototype.append = function (event) {
|
|
76
|
+
this._uncommittedEvents.push(event);
|
|
51
77
|
};
|
|
52
78
|
|
|
53
|
-
EventStream.prototype.hasChanges = function() {
|
|
54
|
-
|
|
79
|
+
EventStream.prototype.hasChanges = function () {
|
|
80
|
+
return this._uncommittedEvents.length > 0;
|
|
55
81
|
};
|
|
56
82
|
|
|
57
|
-
EventStream.prototype.commit = function(commitId, callback) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
83
|
+
EventStream.prototype.commit = function (commitId, callback) {
|
|
84
|
+
var self = this;
|
|
85
|
+
|
|
86
|
+
if(this._isDeleted) {
|
|
87
|
+
throw new Error('Stream is deleted, unable to commit: ' + this._uncommittedEvents.map(function(event) {return event.type}) );
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!this.hasChanges()) {
|
|
91
|
+
//nothing to commit
|
|
92
|
+
return Promise.resolve().nodeify(callback);
|
|
93
|
+
} else {
|
|
94
|
+
var commit = this._buildCommit(commitId, this._uncommittedEvents);
|
|
95
|
+
return this._partition.append(commit, callback).then(function (commit) {
|
|
96
|
+
//rebuild local state
|
|
97
|
+
var events = self._uncommittedEvents;
|
|
98
|
+
self._version = events[events.length - 1].version + 1;
|
|
99
|
+
if (self._writeOnly === true) {
|
|
100
|
+
self._commitSequence++;
|
|
101
|
+
self._clearChanges();
|
|
102
|
+
} else {
|
|
103
|
+
for (var i = 0; i < events.length; i++) {
|
|
104
|
+
self._committedEvents.push(events[i]);
|
|
105
|
+
}
|
|
106
|
+
self._clearChanges();
|
|
107
|
+
self._commitSequence++;
|
|
108
|
+
return self;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
};
|
|
72
113
|
|
|
73
|
-
EventStream.prototype._clearChanges = function() {
|
|
74
|
-
|
|
114
|
+
EventStream.prototype._clearChanges = function () {
|
|
115
|
+
this._uncommittedEvents = [];
|
|
75
116
|
};
|
|
76
117
|
|
|
77
|
-
EventStream.prototype.revertChanges = function() {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
118
|
+
EventStream.prototype.revertChanges = function () {
|
|
119
|
+
//trunc the uncomitted events log
|
|
120
|
+
var arr = this._uncommittedEvents;
|
|
121
|
+
this._uncommittedEvents = [];
|
|
122
|
+
delete arr;
|
|
82
123
|
};
|
|
83
124
|
|
|
125
|
+
EventStream.prototype._buildCommit = function (commitId, events) {
|
|
126
|
+
var commitSequence = this._commitSequence;
|
|
127
|
+
var commit = new Commit(commitId, this._partition._partitionId, this._streamId, ++commitSequence, events);
|
|
128
|
+
var version = this._version == -1 ? 0 : this._version;
|
|
84
129
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
_.forEach(events, function(evt) {
|
|
91
|
-
evt.version = version++;
|
|
92
|
-
})
|
|
93
|
-
return commit;
|
|
130
|
+
_.forEach(events, function (evt) {
|
|
131
|
+
evt.version = version++;
|
|
132
|
+
});
|
|
133
|
+
return commit;
|
|
94
134
|
};
|
|
95
135
|
|
|
96
|
-
EventStream.prototype.getCommittedEvents = function() {
|
|
97
|
-
|
|
136
|
+
EventStream.prototype.getCommittedEvents = function () {
|
|
137
|
+
if (this._writeOnly) {
|
|
138
|
+
throw new Error('Cannot access committed events when using writeOnly mode...');
|
|
139
|
+
}
|
|
140
|
+
return this._committedEvents.slice();
|
|
98
141
|
};
|
|
99
142
|
|
|
100
|
-
EventStream.prototype.getUncommittedEvents = function() {
|
|
101
|
-
|
|
143
|
+
EventStream.prototype.getUncommittedEvents = function () {
|
|
144
|
+
return this._uncommittedEvents.slice();
|
|
102
145
|
};
|
|
103
146
|
|
|
104
|
-
|
|
105
|
-
module.exports = EventStream;
|
|
147
|
+
module.exports = EventStream;
|
|
@@ -9,8 +9,7 @@ var InMemoryPartition = function () {
|
|
|
9
9
|
this._commitIds = [];
|
|
10
10
|
this._commitConcurrencyCheck = [];
|
|
11
11
|
this._snapshots = {};
|
|
12
|
-
}
|
|
13
|
-
|
|
12
|
+
};
|
|
14
13
|
|
|
15
14
|
function getConcurrencyKey(commit) {
|
|
16
15
|
return commit.streamId + '-' + commit.commitSequence;
|
|
@@ -21,7 +20,7 @@ InMemoryPartition.prototype._promisify = function (value, callback) {
|
|
|
21
20
|
|
|
22
21
|
InMemoryPartition.prototype.truncateStreamFrom = function (streamId, commitSequence, callback) {
|
|
23
22
|
var self = this;
|
|
24
|
-
var commits = this._streamIndex[streamId];
|
|
23
|
+
var commits = Array.from(this._streamIndex[streamId]);
|
|
25
24
|
for (var i = 0; i < commits.length; i++) {
|
|
26
25
|
var commit = commits[i];
|
|
27
26
|
|
|
@@ -30,12 +29,17 @@ InMemoryPartition.prototype.truncateStreamFrom = function (streamId, commitSeque
|
|
|
30
29
|
self._commitIds = _.without(self._commitIds, commit.id);
|
|
31
30
|
self._commitConcurrencyCheck = _.without(self._commitConcurrencyCheck, getConcurrencyKey(commit));
|
|
32
31
|
self._commits = _.without(self._commits, commit);
|
|
32
|
+
// i--;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
commits = commits.slice(0, commitSequence);
|
|
35
|
+
commits = commits.slice(0, Math.max(commitSequence, 0));
|
|
36
36
|
this._streamIndex[streamId] = commits;
|
|
37
37
|
return this._promisify(this);
|
|
38
|
-
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// InMemoryPartition.prototype.delete = function (streamId, headers, callback) {
|
|
41
|
+
|
|
42
|
+
// }
|
|
39
43
|
|
|
40
44
|
InMemoryPartition.prototype.applyCommitHeader = function (commitId, header, callback) {
|
|
41
45
|
var self = this;
|
|
@@ -43,17 +47,16 @@ InMemoryPartition.prototype.applyCommitHeader = function (commitId, header, call
|
|
|
43
47
|
if (commit) {
|
|
44
48
|
_.assign(commit, header);
|
|
45
49
|
} else {
|
|
46
|
-
throw new Error(
|
|
50
|
+
throw new Error('Trying to apply header to missing commit: ' + commitId);
|
|
47
51
|
}
|
|
48
52
|
return this._promisify(commit, callback);
|
|
49
|
-
}
|
|
50
|
-
|
|
53
|
+
};
|
|
51
54
|
|
|
52
55
|
InMemoryPartition.prototype.append = function (commit, callback) {
|
|
53
56
|
commit.isDispatched = false;
|
|
54
57
|
//check for duplicates
|
|
55
58
|
if (_.contains(this._commitIds, commit.id)) {
|
|
56
|
-
throw new DuplicateCommitError(
|
|
59
|
+
throw new DuplicateCommitError('Duplicate commit of ' + commit.id);
|
|
57
60
|
}
|
|
58
61
|
var concurrencyKey = getConcurrencyKey(commit);
|
|
59
62
|
if (_.contains(this._commitConcurrencyCheck, concurrencyKey)) {
|
|
@@ -72,7 +75,7 @@ InMemoryPartition.prototype.append = function (commit, callback) {
|
|
|
72
75
|
};
|
|
73
76
|
|
|
74
77
|
InMemoryPartition.prototype.storeSnapshot = function (streamId, snapshot, version, callback) {
|
|
75
|
-
return this._promisify(this._snapshots[streamId] = { id: streamId, version: version, snapshot: snapshot }, callback);
|
|
78
|
+
return this._promisify((this._snapshots[streamId] = { id: streamId, version: version, snapshot: snapshot }), callback);
|
|
76
79
|
};
|
|
77
80
|
|
|
78
81
|
// Loads the latest snapshot
|
|
@@ -100,6 +103,14 @@ InMemoryPartition.prototype.queryAll = function (callback) {
|
|
|
100
103
|
return this._promisify(this._commits.slice(), callback);
|
|
101
104
|
};
|
|
102
105
|
|
|
106
|
+
InMemoryPartition.prototype.getLatestCommit = function (streamId, callback) {
|
|
107
|
+
var result = this._streamIndex[streamId];
|
|
108
|
+
if (result) {
|
|
109
|
+
result = result.slice().pop();
|
|
110
|
+
}
|
|
111
|
+
return this._promisify(result, callback);
|
|
112
|
+
};
|
|
113
|
+
|
|
103
114
|
InMemoryPartition.prototype.queryStream = function (streamId, fromEventSequence, callback) {
|
|
104
115
|
if (_.isFunction(fromEventSequence)) {
|
|
105
116
|
callback = fromEventSequence;
|
|
@@ -132,4 +143,4 @@ InMemoryPartition.prototype.queryStream = function (streamId, fromEventSequence,
|
|
|
132
143
|
return this._promisify(result, callback);
|
|
133
144
|
};
|
|
134
145
|
|
|
135
|
-
module.exports = InMemoryPartition;
|
|
146
|
+
module.exports = InMemoryPartition;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tapeworm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"event",
|
|
25
25
|
"store"
|
|
26
26
|
],
|
|
27
|
+
"prettier": "prettier-config-surikaterna",
|
|
27
28
|
"author": "",
|
|
28
29
|
"license": "MIT",
|
|
29
30
|
"devDependencies": {
|
|
@@ -33,11 +34,13 @@
|
|
|
33
34
|
"eslint-plugin-react": "^3.3.1",
|
|
34
35
|
"istanbul": "^0.3.2",
|
|
35
36
|
"mocha": "^2.0.1",
|
|
36
|
-
"should": "^4.1.0"
|
|
37
|
+
"should": "^4.1.0",
|
|
38
|
+
"prettier": "^2.1.2",
|
|
39
|
+
"prettier-config-surikaterna": "^1.0.1"
|
|
37
40
|
},
|
|
38
41
|
"dependencies": {
|
|
39
42
|
"bluebird": "^2.5.2",
|
|
40
|
-
"lodash": "^2.4.
|
|
41
|
-
"
|
|
43
|
+
"lodash": "^2.4.2",
|
|
44
|
+
"uuid": "^8.3.2"
|
|
42
45
|
}
|
|
43
46
|
}
|
package/test/event_partition.js
CHANGED
|
@@ -1,103 +1,115 @@
|
|
|
1
1
|
var should = require('should');
|
|
2
2
|
var Promise = require('bluebird');
|
|
3
|
-
var uuid = require('
|
|
3
|
+
var uuid = require('uuid').v4;
|
|
4
4
|
|
|
5
5
|
var EventStore = require('..');
|
|
6
6
|
var Commit = EventStore.Commit;
|
|
7
7
|
|
|
8
|
-
describe('Partition', function() {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
8
|
+
describe('Partition', function () {
|
|
9
|
+
describe('#append', function (done) {
|
|
10
|
+
it('should return commit if added', function (done) {
|
|
11
|
+
var es = new EventStore();
|
|
12
|
+
es.openPartition('location')
|
|
13
|
+
.then(function (partition) {
|
|
14
|
+
partition.append(new Commit('1', 'location', '1', 0, [])).then(function (c) {
|
|
15
|
+
c[0].events.length.should.equal(0);
|
|
16
|
+
done();
|
|
17
|
+
});
|
|
18
|
+
})
|
|
19
|
+
.catch(function (err) {
|
|
20
|
+
done(err);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
it('should return after all commits are persisted', function (done) {
|
|
24
|
+
var es = new EventStore();
|
|
25
|
+
es.openPartition('location')
|
|
26
|
+
.then(function (partition) {
|
|
27
|
+
return partition.append([new Commit('1', 'location', '1', 0, []), new Commit('2', 'location', '1', 1, [])]).then(function (c) {
|
|
28
|
+
c.length.should.equal(2);
|
|
29
|
+
done();
|
|
30
|
+
});
|
|
31
|
+
})
|
|
32
|
+
.catch(function (err) {
|
|
33
|
+
done(err);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe('#_truncateStreamFrom', function () {
|
|
38
|
+
it('should remove all commits ', function (done) {
|
|
39
|
+
var es = new EventStore();
|
|
40
|
+
es.openPartition('location')
|
|
41
|
+
.then(function (partition) {
|
|
42
|
+
return partition.append([new Commit('1', 'location', '1', 0, []), new Commit('2', 'location', '1', 1, [])]).then(function (c) {
|
|
43
|
+
return partition._truncateStreamFrom('1', 0).then(function () {
|
|
44
|
+
return partition.openStream('1').then(function (stream) {
|
|
45
|
+
stream.getVersion().should.equal(-1);
|
|
46
|
+
done();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
})
|
|
51
|
+
.catch(function (err) {
|
|
52
|
+
done(err);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
it('should remove all commits after commitSequence', function (done) {
|
|
56
|
+
var es = new EventStore();
|
|
57
|
+
es.openPartition('location')
|
|
58
|
+
.then(function (partition) {
|
|
59
|
+
return partition.append([new Commit('1', 'location', '1', 0, [{}]), new Commit('2', 'location', '1', 1, [{}])]).then(function (c) {
|
|
60
|
+
return partition._truncateStreamFrom('1', 1).then(function () {
|
|
61
|
+
return partition.openStream('1').then(function (stream) {
|
|
62
|
+
stream.getVersion().should.equal(1);
|
|
63
|
+
done();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
})
|
|
68
|
+
.catch(function (err) {
|
|
69
|
+
done(err);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('#_applyCommitHeader', function () {
|
|
74
|
+
it('should add to commit ', function (done) {
|
|
75
|
+
var es = new EventStore();
|
|
76
|
+
es.openPartition('location')
|
|
77
|
+
.then(function (partition) {
|
|
78
|
+
var commit = new Commit('1', 'location', '1', 0, []);
|
|
79
|
+
return partition.append([commit, new Commit('2', 'location', '1', 1, [])]).then(function (c) {
|
|
80
|
+
return partition._applyCommitHeader(commit.id, { authorative: true }).then(function (commit) {
|
|
81
|
+
commit.authorative.should.be.ok;
|
|
82
|
+
done();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
})
|
|
86
|
+
.catch(function (err) {
|
|
87
|
+
done(err);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
it('should throw if commit id is unknown', function (done) {
|
|
91
|
+
var es = new EventStore();
|
|
92
|
+
es.openPartition('location')
|
|
93
|
+
.then(function (partition) {
|
|
94
|
+
var commit = new Commit('1', 'location', '1', 0, []);
|
|
95
|
+
return partition.append([commit, new Commit('2', 'location', '1', 1, [])]).then(function (c) {
|
|
96
|
+
return partition._applyCommitHeader('ID MISSING', { authorative: true }).then(function (commit) {
|
|
97
|
+
done(new Error('Unreachable code'));
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
})
|
|
101
|
+
.catch(function (err) {
|
|
102
|
+
done();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
95
106
|
describe('#queryStreamWithSnapshot', function () {
|
|
96
107
|
it('queryStreamWithSnapshot should return snapshot and missing commits', function (done) {
|
|
97
108
|
var es = new EventStore();
|
|
98
109
|
var streamId = '1';
|
|
99
110
|
var stream;
|
|
100
|
-
es.openPartition('location')
|
|
111
|
+
es.openPartition('location')
|
|
112
|
+
.call('openStream', streamId)
|
|
101
113
|
.then(function (stream1) {
|
|
102
114
|
stream = stream1;
|
|
103
115
|
stream.append({ event: '123' });
|
|
@@ -118,16 +130,18 @@ describe('Partition', function() {
|
|
|
118
130
|
res.commits[0].events.length.should.eql(2);
|
|
119
131
|
res.commits[0].events[0].version.should.eql(2);
|
|
120
132
|
done();
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
})
|
|
136
|
+
.catch(function (err) {
|
|
137
|
+
done(err);
|
|
138
|
+
});
|
|
126
139
|
});
|
|
127
140
|
it('queryStreamWithSnapshot should return snapshot and no commit if up to date', function (done) {
|
|
128
141
|
var es = new EventStore();
|
|
129
142
|
var streamId = '1';
|
|
130
|
-
es.openPartition('location')
|
|
143
|
+
es.openPartition('location')
|
|
144
|
+
.call('openStream', streamId)
|
|
131
145
|
.then(function (stream) {
|
|
132
146
|
stream.append({ event: '123' });
|
|
133
147
|
stream.append({ event: '999' });
|
|
@@ -140,12 +154,79 @@ describe('Partition', function() {
|
|
|
140
154
|
res.snapshot.version.should.eql(2);
|
|
141
155
|
res.commits.length.should.eql(0);
|
|
142
156
|
done();
|
|
143
|
-
})
|
|
144
|
-
})
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
})
|
|
160
|
+
.catch(function (err) {
|
|
161
|
+
done(err);
|
|
162
|
+
});
|
|
148
163
|
});
|
|
149
164
|
});
|
|
165
|
+
describe('#delete', function () {
|
|
166
|
+
it('should delete stream and all commits', function (done) {
|
|
167
|
+
var didIGetaDeleteEvent = false;
|
|
168
|
+
var es = new EventStore(null, (commit) => {
|
|
169
|
+
if (commit.events[0].type === '$stream.deleted.event') {
|
|
170
|
+
didIGetaDeleteEvent = true;
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
es.openPartition('location')
|
|
174
|
+
.then(function (partition) {
|
|
175
|
+
return partition
|
|
176
|
+
.append([new Commit('1', 'location', '1', 0, [{ type: 'dummy.event' }]), new Commit('2', 'location', '1', 1, [{ type: 'dummy2.event' }])])
|
|
177
|
+
.then(function (c) {
|
|
178
|
+
return partition.delete('1', { some: 'header-value', payload: { test: true }, type: 'fail' }).then(function () {
|
|
179
|
+
return partition
|
|
180
|
+
.openStream('1')
|
|
181
|
+
.then(function (stream) {
|
|
182
|
+
console.log(JSON.stringify(stream, null, 2));
|
|
183
|
+
done(new Error('able to open deleted stream'));
|
|
184
|
+
})
|
|
185
|
+
.catch(function (error) {
|
|
186
|
+
error.message.should.equal('Stream is deleted');
|
|
187
|
+
didIGetaDeleteEvent.should.be.true;
|
|
188
|
+
done();
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
})
|
|
193
|
+
.catch(function (err) {
|
|
194
|
+
done(err);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
150
197
|
|
|
198
|
+
it('should delete and placeholder commit should have an id', function (done) {
|
|
199
|
+
var didIGetaDeleteEvent = false;
|
|
200
|
+
var es = new EventStore(null, (commit) => {
|
|
201
|
+
console.log(commit.id);
|
|
202
|
+
if (commit.events[0].type === '$stream.deleted.event' && commit.id) {
|
|
203
|
+
didIGetaDeleteEvent = true;
|
|
204
|
+
}
|
|
205
|
+
should(commit.id).not.be.null;
|
|
206
|
+
});
|
|
207
|
+
es.openPartition('location')
|
|
208
|
+
.then(function (partition) {
|
|
209
|
+
return partition
|
|
210
|
+
.append([new Commit('1', 'location', '1', 0, [{ type: 'dummy.event' }]), new Commit('2', 'location', '1', 1, [{ type: 'dummy2.event' }])])
|
|
211
|
+
.then(function (c) {
|
|
212
|
+
return partition.delete('1', { some: 'header-value', payload: { test: true }, type: 'fail' }).then(function () {
|
|
213
|
+
return partition
|
|
214
|
+
.openStream('1')
|
|
215
|
+
.then(function (stream) {
|
|
216
|
+
console.log(JSON.stringify(stream, null, 2));
|
|
217
|
+
done(new Error('able to open deleted stream'));
|
|
218
|
+
})
|
|
219
|
+
.catch(function (error) {
|
|
220
|
+
error.message.should.equal('Stream is deleted');
|
|
221
|
+
didIGetaDeleteEvent.should.be.true;
|
|
222
|
+
done();
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
})
|
|
227
|
+
.catch(function (err) {
|
|
228
|
+
done(err);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
});
|
|
151
232
|
});
|
package/test/event_stream.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var should = require('should');
|
|
2
2
|
var Promise = require('bluebird');
|
|
3
|
-
var uuid = require('
|
|
3
|
+
var uuid = require('uuid').v4;
|
|
4
4
|
|
|
5
5
|
var EventStore = require('..');
|
|
6
6
|
var EventStream = require('../lib/event_stream');
|
|
@@ -111,6 +111,31 @@ describe('event_stream', function () {
|
|
|
111
111
|
done(err);
|
|
112
112
|
});
|
|
113
113
|
});
|
|
114
|
+
it('event stream writeOnly', function (done) {
|
|
115
|
+
var es = new EventStore();
|
|
116
|
+
var stream;
|
|
117
|
+
es.openPartition('location').then(function (partition) {
|
|
118
|
+
partition.openStream('1', true)
|
|
119
|
+
.then(function (stream1) {
|
|
120
|
+
stream = stream1;
|
|
121
|
+
stream.append({ event: '123' });
|
|
122
|
+
return stream.commit(uuid());
|
|
123
|
+
})
|
|
124
|
+
.then(function () {
|
|
125
|
+
stream._commitSequence.should.equal(0);
|
|
126
|
+
stream.append({ event: '666' });
|
|
127
|
+
stream.append({ event: '777' });
|
|
128
|
+
return stream.commit(uuid());
|
|
129
|
+
})
|
|
130
|
+
.then(function () {
|
|
131
|
+
stream._commitSequence.should.equal(1);
|
|
132
|
+
done();
|
|
133
|
+
})
|
|
134
|
+
.catch(function (err) {
|
|
135
|
+
done(err);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
|
114
139
|
it('committed events should have increasing version', function (done) {
|
|
115
140
|
var es = new EventStore();
|
|
116
141
|
var stream;
|
|
@@ -127,11 +152,37 @@ describe('event_stream', function () {
|
|
|
127
152
|
})
|
|
128
153
|
.then(function () {
|
|
129
154
|
stream.getCommittedEvents()[3].version.should.equal(3);
|
|
155
|
+
stream._version.should.equal(4);
|
|
130
156
|
done();
|
|
131
157
|
}).catch(function (err) {
|
|
132
158
|
done(err);
|
|
133
159
|
});
|
|
134
160
|
});
|
|
161
|
+
it('committed events should have increasing version (writeOnly)', function (done) {
|
|
162
|
+
var es = new EventStore();
|
|
163
|
+
var stream;
|
|
164
|
+
es.openPartition('location').then(function (partition) {
|
|
165
|
+
partition.openStream('1', true)
|
|
166
|
+
.then(function (stream1) {
|
|
167
|
+
stream = stream1;
|
|
168
|
+
stream.append({ event: '123' });
|
|
169
|
+
stream.append({ event: '999' });
|
|
170
|
+
return stream.commit(uuid());
|
|
171
|
+
})
|
|
172
|
+
.then(function () {
|
|
173
|
+
stream.append({ event: '666' });
|
|
174
|
+
stream.append({ event: '777' });
|
|
175
|
+
return stream.commit(uuid());
|
|
176
|
+
})
|
|
177
|
+
.then(function () {
|
|
178
|
+
stream._version.should.equal(4);
|
|
179
|
+
done();
|
|
180
|
+
})
|
|
181
|
+
.catch(function (err) {
|
|
182
|
+
done(err);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
135
186
|
it('published events should have increasing version', function (done) {
|
|
136
187
|
var commitCount = 0;
|
|
137
188
|
|
|
@@ -160,4 +211,4 @@ describe('event_stream', function () {
|
|
|
160
211
|
});
|
|
161
212
|
});
|
|
162
213
|
});
|
|
163
|
-
});
|
|
214
|
+
});
|