presidium 0.15.27 → 0.15.31

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/DynamoStream.js CHANGED
@@ -144,20 +144,20 @@ DynamoStream.prototype.getRecords = async function* getRecords(
144
144
  */
145
145
 
146
146
  }).promise().then(get('ShardIterator'))
147
+
147
148
  let records = await this.client.getRecords({
148
149
  ShardIterator: startingShardIterator,
149
150
  Limit: this.getRecordsLimit
150
151
  }).promise()
151
-
152
152
  if (records.Records.length > 0) {
153
153
  yield* records.Records.map(assign({
154
154
  table: always(this.table),
155
155
  shardId: always(Shard.ShardId),
156
156
  }))
157
157
  }
158
+ await new Promise(resolve => setTimeout(resolve, this.getRecordsInterval))
158
159
 
159
160
  while (!this.closed && records.NextShardIterator != null) {
160
- await new Promise(resolve => setTimeout(resolve, this.getRecordsInterval))
161
161
  records = await this.client.getRecords({
162
162
  ShardIterator: records.NextShardIterator,
163
163
  Limit: this.getRecordsLimit
@@ -168,6 +168,7 @@ DynamoStream.prototype.getRecords = async function* getRecords(
168
168
  shardId: always(Shard.ShardId),
169
169
  }))
170
170
  }
171
+ await new Promise(resolve => setTimeout(resolve, this.getRecordsInterval))
171
172
  }
172
173
  }
173
174
 
package/KinesisStream.js CHANGED
@@ -29,11 +29,6 @@ const {
29
29
  * endpoint: string,
30
30
  * shardIteratorType: 'AT_SEQUENCE_NUMBER'|'AFTER_SEQUENCE_NUMBER'|'TRIM_HORIZON'|'LATEST'|'AT_TIMESTAMP',
31
31
  * timestamp: Date|string|number, // find events at date (requires shardIteratorType 'AT_TIMESTAMP')
32
- * startingSequenceNumber: string, // find events at data record (requires shardIteratorType 'AT_SEQUENCE_NUMBER' or 'AFTER_SEQUENCE_NUMBER')
33
- * shardFilterType: 'AFTER_SHARD_ID'|'AT_TRIM_HORIZON'|'FROM_TRIM_HORIZON'|'AT_LATEST'|'AT_TIMESTAMP'|'FROM_TIMESTAMP',
34
- * shardFilterShardId: string,
35
- * shardFilterTimestamp: Date|string|number,
36
- * streamCreationTimestamp: Date|string|number, // distinguishes streams of same name e.g. after deleting
37
32
  * }) -> KinesisStream
38
33
  * ```
39
34
  *
@@ -53,13 +48,8 @@ const KinesisStream = function (options) {
53
48
  this.shardUpdatePeriod = options.shardUpdatePeriod ?? 15000
54
49
  this.getRecordsInterval = options.getRecordsInterval ?? 1000
55
50
  this.shardIteratorType = options.shardIteratorType ?? 'LATEST'
56
- this.shardIteratorTimestamp = options.shardIteratorTimestamp
57
- this.shardFilterType = options.shardFilterType
58
- this.shardFilterShardId = options.shardFilterShardId
59
- this.shardFilterTimestamp = options.shardFilterTimestamp
60
- this.streamCreationTimestamp = options.streamCreationTimestamp
51
+ this.shardCount = options.shardCount ?? 1
61
52
  this.timestamp = options.timestamp
62
- this.startingSequenceNumber = options.startingSequenceNumber
63
53
  this.kinesis = new Kinesis(omit(['name'])(options))
64
54
  this.cancelToken = new Promise((_, reject) => (this.canceller = reject))
65
55
 
@@ -76,7 +66,7 @@ const KinesisStream = function (options) {
76
66
  }).catch(async () => {
77
67
  await this.kinesis.client.createStream({
78
68
  StreamName: this.name,
79
- ShardCount: 1,
69
+ ShardCount: this.shardCount,
80
70
  }).promise()
81
71
  await this.kinesis.client.waitFor('streamExists', {
82
72
  StreamName: this.name
@@ -103,7 +93,7 @@ KinesisStream.prototype.delete = function deleteStream() {
103
93
  * @synopsis
104
94
  * ```coffeescript [specscript]
105
95
  * KinesisStream(options).putRecord(
106
- * data string|binary,
96
+ * data string|Buffer,
107
97
  * options {
108
98
  * partitionKey: string, // input to aws hash function to determine which shard
109
99
  * explicitHashKey: string, // skips aws hash function to determine which shard
@@ -129,6 +119,57 @@ KinesisStream.prototype.putRecord = async function putRecord(data, options = {})
129
119
  }).promise()
130
120
  }
131
121
 
122
+ /**
123
+ * @name KinesisStream.prototype.putRecords
124
+ *
125
+ * @synopsis
126
+ * ```coffeescript [specscript]
127
+ * KinesisStream(options).putRecords(records Array<{
128
+ * data: string|Buffer,
129
+ * partitionKey: string, // input to aws hash function to determine which shard
130
+ * explicitHashKey: string, // skips aws hash function to determine which shard
131
+ * }>) -> Promise<{
132
+ * FailedRecordCount: number, // number of unsuccessfully processed records
133
+ * Records: Array<{
134
+ * SequenceNumber: string,
135
+ * ShardId: string,
136
+ * ErrorCode?: 'ProvisionedThroughputExceededException'|'InternalFailure',
137
+ * ErrorMessage?: string,
138
+ * }>,
139
+ * EncryptionType: 'NONE'|'KMS',
140
+ * }>
141
+ * ```
142
+ *
143
+ * @description
144
+ * Limits/Quotas:
145
+ * * 500 records max per request
146
+ * * 1 MB per record max
147
+ * * 5 MB per request max
148
+ * * 1000 records per second per shard
149
+ * * 1 MB per second per shard
150
+ *
151
+ * https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Kinesis.html#putRecords-property
152
+ */
153
+ KinesisStream.prototype.putRecords = async function putRecords(records) {
154
+ return this.kinesis.client.putRecords({
155
+ StreamName: this.name,
156
+ Records: records.map(({ data, partitionKey, explicitHashKey }) => ({
157
+ Data: data,
158
+ PartitionKey: partitionKey ?? data.slice(0, 255),
159
+ ...explicitHashKey && { ExplicitHashKey: explicitHashKey },
160
+ }))
161
+ }).promise().then(tap(response => {
162
+ if (response.Records.some(has('ErrorCode'))) {
163
+ const errors = response.Records.filter(has('ErrorCode')).map(Record => {
164
+ const error = new Error(Record.ErrorMessage)
165
+ error.code = Record.ErrorCode
166
+ return error
167
+ })
168
+ throw new AggregateError(errors, 'Some records failed to process')
169
+ }
170
+ }))
171
+ }
172
+
132
173
  // () => ()
133
174
  KinesisStream.prototype.close = function close() {
134
175
  this.closed = true
@@ -163,9 +204,7 @@ KinesisStream.prototype.getRecords = async function* getRecords(Shard) {
163
204
  ShardId: Shard.ShardId,
164
205
  StreamName: this.name,
165
206
  ShardIteratorType: this.shardIteratorType,
166
- ...this.shardIteratorTimestamp && {
167
- Timestamp: this.shardIteratorTimestamp,
168
- },
207
+ ...this.timestamp == null ? {} : { Timestamp: this.timestamp },
169
208
  }).promise().then(get('ShardIterator'))
170
209
 
171
210
  let records = await this.kinesis.client.getRecords({
@@ -175,9 +214,9 @@ KinesisStream.prototype.getRecords = async function* getRecords(Shard) {
175
214
  if (records.Records.length > 0) {
176
215
  yield* records.Records
177
216
  }
217
+ await new Promise(resolve => setTimeout(resolve, this.getRecordsInterval))
178
218
 
179
219
  while (!this.closed && records.NextShardIterator != null) {
180
- await new Promise(resolve => setTimeout(resolve, this.getRecordsInterval))
181
220
  records = await this.kinesis.client.getRecords({
182
221
  ShardIterator: records.NextShardIterator,
183
222
  Limit: this.getRecordsLimit,
@@ -185,6 +224,7 @@ KinesisStream.prototype.getRecords = async function* getRecords(Shard) {
185
224
  if (records.Records.length > 0) {
186
225
  yield* records.Records
187
226
  }
227
+ await new Promise(resolve => setTimeout(resolve, this.getRecordsInterval))
188
228
  }
189
229
  }
190
230
 
@@ -7,9 +7,11 @@ const map = require('rubico/map')
7
7
  const thunkify = require('rubico/thunkify')
8
8
 
9
9
  const test = new Test('KinesisStream', KinesisStream)
10
+
10
11
  .before(function () {
11
12
  this.streams = []
12
13
  })
14
+
13
15
  .case({
14
16
  name: 'my-stream',
15
17
  endpoint: 'http://localhost:4567',
@@ -21,7 +23,87 @@ const test = new Test('KinesisStream', KinesisStream)
21
23
  await myStream.putRecord('hey')
22
24
  await myStream.putRecord('ho', { partitionKey: 'ho' })
23
25
  await myStream.putRecord('hi', { explicitHashKey: '127' })
26
+ const first3 = await asyncIterableTake(3)(myStream)
27
+ const first3Again = await asyncIterableTake(3)(myStream)
28
+ assert.deepEqual(first3, first3Again)
29
+ this.streams.push(myStream)
30
+ })
31
+
32
+ .case({
33
+ name: 'my-stream',
34
+ endpoint: 'http://localhost:4567',
35
+ shardIteratorType: 'TRIM_HORIZON',
36
+ getRecordsLimit: 1,
37
+ listShardsLimit: 1,
38
+ shardCount: 2,
39
+ }, async function (myStream) {
40
+ await myStream.ready
41
+ await myStream.putRecord('hey', { partitionKey: 'a' })
42
+ await myStream.putRecord('ho', { partitionKey: 'a' })
43
+ await myStream.putRecord('hi', { partitionKey: 'a' })
44
+ const first3 = await asyncIterableTake(3)(myStream)
45
+ const first3Again = await asyncIterableTake(3)(myStream)
46
+ assert.deepEqual(first3, first3Again)
47
+ this.streams.push(myStream)
48
+ })
24
49
 
50
+ .case({
51
+ name: 'my-stream',
52
+ endpoint: 'http://localhost:4567',
53
+ shardIteratorType: 'TRIM_HORIZON',
54
+ }, async function (myStream) {
55
+ await myStream.ready
56
+ await myStream.putRecords([
57
+ { data: 'hey' },
58
+ { data: 'ho', partitionKey: 'ho' },
59
+ { data: 'hi', explicitHashKey: '127' },
60
+ ])
61
+ const first3 = await asyncIterableTake(3)(myStream)
62
+ const first3Again = await asyncIterableTake(3)(myStream)
63
+ assert.deepEqual(first3, first3Again)
64
+ this.streams.push(myStream)
65
+ })
66
+
67
+ .case({
68
+ name: 'my-stream',
69
+ endpoint: 'http://localhost:4567',
70
+ shardIteratorType: 'TRIM_HORIZON',
71
+ }, async function (myStream) {
72
+ myStream.kinesis.client.putRecords = () => ({
73
+ promise: async () => ({
74
+ Records: [
75
+ {
76
+ ErrorCode: 'ProvisionedThroughputExceededException',
77
+ ErrorMessage: 'Some message with accountId, stream name, and shard ID',
78
+ },
79
+ {
80
+ ErrorCode: 'ProvisionedThroughputExceededException',
81
+ ErrorMessage: 'Some message with accountId, stream name, and shard ID',
82
+ },
83
+ ],
84
+ }),
85
+ })
86
+ await myStream.ready
87
+ await assert.rejects(
88
+ () => myStream.putRecords([{ data: 'hey' }]),
89
+ new AggregateError([
90
+ new Error('Some message with accountId, stream name, and shard ID'),
91
+ new Error('Some message with accountId, stream name, and shard ID'),
92
+ ], 'Some records failed to process')
93
+ )
94
+ this.streams.push(myStream)
95
+ })
96
+
97
+ .case({
98
+ name: 'my-stream',
99
+ endpoint: 'http://localhost:4567',
100
+ shardIteratorType: 'AT_TIMESTAMP',
101
+ timestamp: new Date(Date.now() - 5000),
102
+ }, async function (myStream) {
103
+ await myStream.ready
104
+ await myStream.putRecord('hey')
105
+ await myStream.putRecord('ho', { partitionKey: 'ho' })
106
+ await myStream.putRecord('hi', { explicitHashKey: '127' })
25
107
  const first3 = await asyncIterableTake(3)(myStream)
26
108
  const first3Again = await asyncIterableTake(3)(myStream)
27
109
  assert.deepEqual(first3, first3Again)
@@ -69,8 +151,11 @@ const test = new Test('KinesisStream', KinesisStream)
69
151
  await new Promise(resolve => setTimeout(resolve, 1000))
70
152
  })
71
153
 
72
- .after(async function() {
73
- await map(stream => stream.delete())(this.streams)
154
+ .after(async function () {
155
+ await map(async function cleanup(stream) {
156
+ stream.close()
157
+ await stream.delete()
158
+ })(this.streams)
74
159
  })
75
160
 
76
161
  if (process.argv[1] == __filename) {
@@ -177,6 +177,10 @@ TranscribeStream.prototype.sendAudioChunk = function (chunk) {
177
177
  this.websocket.send(bytes)
178
178
  }
179
179
 
180
+ TranscribeStream.prototype.close = function () {
181
+ this.websocket.close()
182
+ }
183
+
180
184
  /**
181
185
  * @name marshalHeaders
182
186
  *
@@ -53,7 +53,7 @@ const test = new Test('TranscribeStream', async function () {
53
53
  wav.fromMuLaw()
54
54
  testTranscribeStream.sendAudioChunk(Buffer.from(wav.data.samples))
55
55
  } else if (event.event == 'stop') {
56
- testTranscribeStream.websocket.close()
56
+ testTranscribeStream.close()
57
57
  }
58
58
  })
59
59
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "presidium",
3
- "version": "0.15.27",
3
+ "version": "0.15.31",
4
4
  "description": "A library for creating web services",
5
5
  "author": "Richard Tong",
6
6
  "license": "MIT",