presidium 0.15.28 → 0.15.32
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/KinesisStream.js +55 -16
- package/KinesisStream.test.js +87 -2
- package/TranscribeStream.js +10 -1
- package/TranscribeStream.test.js +9 -2
- package/package.json +1 -1
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.
|
|
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:
|
|
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|
|
|
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.
|
|
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({
|
package/KinesisStream.test.js
CHANGED
|
@@ -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(
|
|
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) {
|
package/TranscribeStream.js
CHANGED
|
@@ -115,7 +115,12 @@ const TranscribeStream = function (options) {
|
|
|
115
115
|
})
|
|
116
116
|
this.websocket.on('message', chunk => {
|
|
117
117
|
const { headers, body } = unmarshalMessage(chunk)
|
|
118
|
-
if (
|
|
118
|
+
if (headers[':message-type'] == 'exception') {
|
|
119
|
+
const error = new Error(body.Message)
|
|
120
|
+
error.name = headers[':exception-type']
|
|
121
|
+
this.emit('error', error)
|
|
122
|
+
}
|
|
123
|
+
else if (body.Transcript.Results.length > 0) {
|
|
119
124
|
if (body.Transcript.Results[0].IsPartial) {
|
|
120
125
|
this.emit('partialTranscription', body.Transcript.Results[0])
|
|
121
126
|
} else {
|
|
@@ -177,6 +182,10 @@ TranscribeStream.prototype.sendAudioChunk = function (chunk) {
|
|
|
177
182
|
this.websocket.send(bytes)
|
|
178
183
|
}
|
|
179
184
|
|
|
185
|
+
TranscribeStream.prototype.close = function () {
|
|
186
|
+
this.websocket.close()
|
|
187
|
+
}
|
|
188
|
+
|
|
180
189
|
/**
|
|
181
190
|
* @name marshalHeaders
|
|
182
191
|
*
|
package/TranscribeStream.test.js
CHANGED
|
@@ -52,8 +52,6 @@ const test = new Test('TranscribeStream', async function () {
|
|
|
52
52
|
wav.fromScratch(1, 8000, '8', Buffer.from(event.media.payload, 'base64'))
|
|
53
53
|
wav.fromMuLaw()
|
|
54
54
|
testTranscribeStream.sendAudioChunk(Buffer.from(wav.data.samples))
|
|
55
|
-
} else if (event.event == 'stop') {
|
|
56
|
-
testTranscribeStream.websocket.close()
|
|
57
55
|
}
|
|
58
56
|
})
|
|
59
57
|
|
|
@@ -64,6 +62,15 @@ const test = new Test('TranscribeStream', async function () {
|
|
|
64
62
|
})
|
|
65
63
|
assert.equal(testTranscription, 'Hello, world.')
|
|
66
64
|
|
|
65
|
+
// wait for timeout error to test error handling
|
|
66
|
+
await new Promise(resolve => {
|
|
67
|
+
testTranscribeStream.on('error', error => {
|
|
68
|
+
console.error(error)
|
|
69
|
+
testTranscribeStream.close()
|
|
70
|
+
resolve()
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
67
74
|
/*
|
|
68
75
|
// fill media-stream-fixture-aws-keynote.txt
|
|
69
76
|
const Twilio = require('@claimyr_hq/twilio/Twilio')
|