@creator.co/wapi 1.7.9 → 1.7.10-alpha.2
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/.eslintrc.cjs +19 -0
- package/README.md +1 -11
- package/dist/index.d.ts +11 -6
- package/dist/index.js +11 -6
- package/dist/index.js.map +1 -1
- package/dist/package-lock.json +830 -223
- package/dist/package.json +3 -1
- package/dist/src/BaseEvent/DynamoTransaction.d.ts +70 -0
- package/dist/src/BaseEvent/DynamoTransaction.js +104 -0
- package/dist/src/BaseEvent/DynamoTransaction.js.map +1 -0
- package/dist/src/BaseEvent/EventProcessor.js.map +1 -1
- package/dist/src/BaseEvent/Transaction.d.ts +3 -3
- package/dist/src/BaseEvent/Transaction.js +1 -1
- package/dist/src/BaseEvent/Transaction.js.map +1 -1
- package/dist/src/Cache/Redis.js.map +1 -1
- package/dist/src/Database/integrations/dynamo/DynamoDatabase.js.map +1 -1
- package/dist/src/Database/integrations/knex/KnexDatabase.js.map +1 -1
- package/dist/src/Database/integrations/kysely/KyselyDatabase.js.map +1 -1
- package/dist/src/Database/integrations/pgsql/PostgresDatabase.js.map +1 -1
- package/dist/src/Logger/Logger.js.map +1 -1
- package/dist/src/Publisher/Publisher.js.map +1 -1
- package/dist/src/Server/RouteResolver.d.ts +0 -1
- package/dist/src/Server/RouteResolver.js +0 -1
- package/dist/src/Server/RouteResolver.js.map +1 -1
- package/dist/src/Server/lib/container/Proxy.js.map +1 -1
- package/dist/src/Util/Utils.d.ts +15 -0
- package/dist/src/Util/Utils.js +41 -0
- package/dist/src/Util/Utils.js.map +1 -1
- package/index.ts +11 -5
- package/package.json +3 -1
- package/src/BaseEvent/DynamoTransaction.ts +175 -0
- package/src/BaseEvent/EventProcessor.ts +1 -1
- package/src/BaseEvent/Transaction.ts +7 -3
- package/src/Cache/Redis.ts +1 -0
- package/src/Database/integrations/dynamo/DynamoDatabase.ts +1 -1
- package/src/Database/integrations/knex/KnexDatabase.ts +1 -1
- package/src/Database/integrations/kysely/KyselyDatabase.ts +3 -1
- package/src/Database/integrations/pgsql/PostgresDatabase.ts +2 -1
- package/src/Logger/Logger.ts +7 -7
- package/src/Publisher/Publisher.ts +5 -2
- package/src/Server/RouteResolver.ts +1 -1
- package/src/Server/lib/container/Proxy.ts +2 -2
- package/src/Util/AsyncSingleton.ts +1 -1
- package/src/Util/Utils.ts +38 -0
- package/tests/API/Utils.test.ts +100 -38
- package/tests/BaseEvent/DynamoTransaction.test.ts +272 -0
- package/tests/Logger/Logger.test.ts +1 -1
- package/tests/Test.utils.ts +5 -1
package/tests/API/Utils.test.ts
CHANGED
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
import { expect } from 'chai'
|
|
1
|
+
import { expect as c_expect } from 'chai'
|
|
2
2
|
|
|
3
3
|
import Utils from '../../src/Util/Utils.js'
|
|
4
4
|
|
|
5
5
|
describe('isHybridlessContainer', () => {
|
|
6
6
|
test('Simple isHybridlessContainer test', () => {
|
|
7
7
|
const v = Utils.isHybridlessContainer()
|
|
8
|
-
|
|
8
|
+
c_expect(v).to.be.a('boolean')
|
|
9
9
|
// this is done, so tests are reliable based on the testing .env
|
|
10
|
-
|
|
10
|
+
c_expect(v).to.be[process.env.HYBRIDLESS_RUNTIME ? 'true' : 'false']
|
|
11
11
|
})
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
describe('isValidString', () => {
|
|
15
15
|
test('Empty string is not valid', () => {
|
|
16
16
|
const v = Utils.isValidString('')
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
c_expect(v).to.be.a('boolean')
|
|
18
|
+
c_expect(v).to.be.false
|
|
19
19
|
})
|
|
20
20
|
|
|
21
21
|
test('Number is not a valid string', () => {
|
|
22
22
|
// @ts-ignore
|
|
23
23
|
const v = Utils.isValidString(12)
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
c_expect(v).to.be.a('boolean')
|
|
25
|
+
c_expect(v).to.be.false
|
|
26
26
|
})
|
|
27
27
|
|
|
28
28
|
test('Simple string is valid', () => {
|
|
29
29
|
const v = Utils.isValidString('abc')
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
c_expect(v).to.be.a('boolean')
|
|
31
|
+
c_expect(v).to.be.true
|
|
32
32
|
})
|
|
33
33
|
})
|
|
34
34
|
|
|
35
35
|
describe('parseIntNullIfNaN', () => {
|
|
36
36
|
test('Null if only chars', () => {
|
|
37
37
|
const v = Utils.parseIntNullIfNaN('abc')
|
|
38
|
-
|
|
38
|
+
c_expect(v).to.be.null
|
|
39
39
|
})
|
|
40
40
|
|
|
41
41
|
test('Null if chars with numbers', () => {
|
|
42
42
|
const v = Utils.parseIntNullIfNaN('abc123')
|
|
43
|
-
|
|
43
|
+
c_expect(v).to.be.null
|
|
44
44
|
})
|
|
45
45
|
|
|
46
46
|
test('Null if null', () => {
|
|
47
47
|
// @ts-ignore
|
|
48
48
|
const v = Utils.parseIntNullIfNaN(null)
|
|
49
|
-
|
|
49
|
+
c_expect(v).to.be.null
|
|
50
50
|
})
|
|
51
51
|
|
|
52
52
|
test('Not null if starts with numbers (js behavior)', () => {
|
|
53
53
|
const v = Utils.parseIntNullIfNaN('123abc')
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
c_expect(v).to.be.a('number')
|
|
55
|
+
c_expect(v).to.be.equals(123)
|
|
56
56
|
})
|
|
57
57
|
|
|
58
58
|
test('Not null if numbers', () => {
|
|
59
59
|
const v = Utils.parseIntNullIfNaN('123')
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
c_expect(v).to.be.a('number')
|
|
61
|
+
c_expect(v).to.be.equals(123)
|
|
62
62
|
})
|
|
63
63
|
|
|
64
64
|
test('Not null if number type', () => {
|
|
65
65
|
// @ts-ignore
|
|
66
66
|
const v = Utils.parseIntNullIfNaN(123)
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
c_expect(v).to.be.a('number')
|
|
68
|
+
c_expect(v).to.be.equals(123)
|
|
69
69
|
})
|
|
70
70
|
})
|
|
71
71
|
|
|
72
72
|
describe('parseObjectNullIfEmpty', () => {
|
|
73
73
|
test('Null if only chars', () => {
|
|
74
74
|
const v = Utils.parseObjectNullIfEmpty('abc')
|
|
75
|
-
|
|
75
|
+
c_expect(v).to.be.null
|
|
76
76
|
})
|
|
77
77
|
|
|
78
78
|
test('Null if chars with numbers', () => {
|
|
79
79
|
const v = Utils.parseObjectNullIfEmpty('abc123')
|
|
80
|
-
|
|
80
|
+
c_expect(v).to.be.null
|
|
81
81
|
})
|
|
82
82
|
|
|
83
83
|
test('Null if null', () => {
|
|
84
84
|
// @ts-ignore
|
|
85
85
|
const v = Utils.parseObjectNullIfEmpty(null)
|
|
86
|
-
|
|
86
|
+
c_expect(v).to.be.null
|
|
87
87
|
})
|
|
88
88
|
|
|
89
89
|
test('Null if malformed object', () => {
|
|
90
90
|
const v = Utils.parseObjectNullIfEmpty('{d: 123}')
|
|
91
|
-
|
|
91
|
+
c_expect(v).to.be.null
|
|
92
92
|
})
|
|
93
93
|
|
|
94
94
|
test('Null if empty object', () => {
|
|
95
95
|
const v = Utils.parseObjectNullIfEmpty('{}')
|
|
96
|
-
|
|
96
|
+
c_expect(v).to.be.null
|
|
97
97
|
})
|
|
98
98
|
|
|
99
99
|
test('Not null if object with a key', () => {
|
|
100
100
|
const v = Utils.parseObjectNullIfEmpty('{"d": 123}')
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
c_expect(v).to.be.a('object')
|
|
102
|
+
c_expect(v?.['d']).to.be.equals(123)
|
|
103
103
|
})
|
|
104
104
|
})
|
|
105
105
|
|
|
106
106
|
describe('isValidNumber', () => {
|
|
107
107
|
test('Not a valid number if only chars', () => {
|
|
108
108
|
const v = Utils.isValidNumber('abc')
|
|
109
|
-
|
|
109
|
+
c_expect(v).to.be.false
|
|
110
110
|
})
|
|
111
111
|
|
|
112
112
|
test('Not a valid number if only chars and numbers', () => {
|
|
113
113
|
const v = Utils.isValidNumber('abc123')
|
|
114
|
-
|
|
114
|
+
c_expect(v).to.be.false
|
|
115
115
|
})
|
|
116
116
|
|
|
117
117
|
test('Not a valid number if causes a crash on conversion', () => {
|
|
@@ -121,47 +121,109 @@ describe('isValidNumber', () => {
|
|
|
121
121
|
throw new Error('Evil object')
|
|
122
122
|
},
|
|
123
123
|
})
|
|
124
|
-
|
|
124
|
+
c_expect(v).to.be.false
|
|
125
125
|
})
|
|
126
126
|
|
|
127
127
|
test('Valid number if numbers and chars after', () => {
|
|
128
128
|
const v = Utils.isValidNumber('123abc')
|
|
129
|
-
|
|
129
|
+
c_expect(v).to.be.true
|
|
130
130
|
})
|
|
131
131
|
|
|
132
132
|
test('Valid number if numbers', () => {
|
|
133
133
|
const v = Utils.isValidNumber('123')
|
|
134
|
-
|
|
134
|
+
c_expect(v).to.be.true
|
|
135
135
|
})
|
|
136
136
|
})
|
|
137
137
|
|
|
138
138
|
describe('caseInsensitiveObjectForKey', () => {
|
|
139
139
|
test('Get a key that is lowercase, asking uppercase', () => {
|
|
140
140
|
const v = Utils.caseInsensitiveObjectForKey({ abc: 123 }, 'ABC')
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
c_expect(v).to.be.a('number')
|
|
142
|
+
c_expect(v).to.be.equals(123)
|
|
143
143
|
})
|
|
144
144
|
|
|
145
145
|
test('Get a key that is uppercase, asking lowercase', () => {
|
|
146
146
|
const v = Utils.caseInsensitiveObjectForKey({ ABC: 123 }, 'abc')
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
c_expect(v).to.be.a('number')
|
|
148
|
+
c_expect(v).to.be.equals(123)
|
|
149
149
|
})
|
|
150
150
|
|
|
151
151
|
test('Fails gracefully if key is not found', () => {
|
|
152
152
|
const v = Utils.caseInsensitiveObjectForKey({ ABC: 123 }, 'asd')
|
|
153
|
-
|
|
153
|
+
c_expect(v).to.be.null
|
|
154
154
|
})
|
|
155
155
|
|
|
156
156
|
test('Fails gracefully for null object', () => {
|
|
157
157
|
const v = Utils.caseInsensitiveObjectForKey(null, 'abc')
|
|
158
|
-
|
|
158
|
+
c_expect(v).to.be.null
|
|
159
159
|
})
|
|
160
160
|
|
|
161
161
|
test('Fails gracefully for invalid object', () => {
|
|
162
162
|
const v = Utils.caseInsensitiveObjectForKey('', 'abc')
|
|
163
|
-
|
|
163
|
+
c_expect(v).to.be.null
|
|
164
164
|
})
|
|
165
165
|
})
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
describe('ddbMarshall', () => {
|
|
168
|
+
test('Marshalling of a simple object', () => {
|
|
169
|
+
const input = { key: 'value' }
|
|
170
|
+
const output = Utils.ddbMarshall(input)
|
|
171
|
+
expect(output).toEqual({ key: { S: 'value' } })
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
test('Marshalling of an array', () => {
|
|
175
|
+
const input = [1, 2, 3]
|
|
176
|
+
const output = Utils.ddbMarshall(input)
|
|
177
|
+
expect(output).toEqual({ L: [{ N: '1' }, { N: '2' }, { N: '3' }] })
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test('Marshalling of a nested object', () => {
|
|
181
|
+
const input = { key: { nestedKey: 'nestedValue' } }
|
|
182
|
+
const output = Utils.ddbMarshall(input)
|
|
183
|
+
expect(output).toEqual({ key: { M: { nestedKey: { S: 'nestedValue' } } } })
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('Marshalling of a complex object with undefined values', () => {
|
|
187
|
+
const input = { key1: 'value1', key2: undefined, key3: null }
|
|
188
|
+
const output = Utils.ddbMarshall(input)
|
|
189
|
+
expect(output).toEqual({ key1: { S: 'value1' }, key3: { NULL: true } })
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
test('Marshalling of a primitive value', () => {
|
|
193
|
+
const input = 42
|
|
194
|
+
const output = Utils.ddbMarshall(input)
|
|
195
|
+
expect(output).toEqual({ N: '42' })
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
describe('ddbUnmarshall', () => {
|
|
200
|
+
test('Unmarshalling of a simple DynamoDB object', () => {
|
|
201
|
+
const input = { key: { S: 'value' } }
|
|
202
|
+
const output = Utils.ddbUnmarshall(input)
|
|
203
|
+
expect(output).toEqual({ key: 'value' })
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
test('Unmarshalling of a DynamoDB array', () => {
|
|
207
|
+
const input = { array: { L: [{ N: '1' }, { N: '2' }, { N: '3' }] } }
|
|
208
|
+
const output = Utils.ddbUnmarshall(input)
|
|
209
|
+
expect(output.array).toEqual([1, 2, 3])
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
test('Unmarshalling of a nested DynamoDB object', () => {
|
|
213
|
+
const input = { key: { M: { nestedKey: { S: 'nestedValue' } } } }
|
|
214
|
+
const output = Utils.ddbUnmarshall(input)
|
|
215
|
+
expect(output).toEqual({ key: { nestedKey: 'nestedValue' } })
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
test('Unmarshalling of a complex DynamoDB object with null values', () => {
|
|
219
|
+
const input = { key1: { S: 'value1' }, key3: { NULL: true } }
|
|
220
|
+
const output = Utils.ddbUnmarshall(input)
|
|
221
|
+
expect(output).toEqual({ key1: 'value1', key3: null })
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
test('Unmarshalling of a primitive value', () => {
|
|
225
|
+
const input = { key: { N: '42' } }
|
|
226
|
+
const output = Utils.ddbUnmarshall(input)
|
|
227
|
+
expect(output.key).toEqual(42)
|
|
228
|
+
})
|
|
229
|
+
})
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { expect as c_expect } from 'chai'
|
|
2
|
+
|
|
3
|
+
import Response from '../../src/API/Response.js'
|
|
4
|
+
import DynamoTransaction from '../../src/BaseEvent/DynamoTransaction.js'
|
|
5
|
+
import Globals from '../../src/Globals.js'
|
|
6
|
+
import Utils from '../../src/Util/Utils.js'
|
|
7
|
+
import { emptyDynamoDBEvent, observableContext } from '../Test.utils.js'
|
|
8
|
+
|
|
9
|
+
const skipCleanTmp = {
|
|
10
|
+
skipCleanTmp: true,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe('DynamoTransaction success invocation path', () => {
|
|
14
|
+
test('Simple success', async () => {
|
|
15
|
+
const record = { id: '123' }
|
|
16
|
+
const context = observableContext()
|
|
17
|
+
const transaction = new DynamoTransaction(
|
|
18
|
+
emptyDynamoDBEvent({
|
|
19
|
+
Records: [
|
|
20
|
+
{ dynamodb: { NewImage: Utils.ddbMarshall(record) } },
|
|
21
|
+
{ dynamodb: { NewImage: Utils.ddbMarshall(record) } },
|
|
22
|
+
],
|
|
23
|
+
}),
|
|
24
|
+
context
|
|
25
|
+
)
|
|
26
|
+
let count = 0
|
|
27
|
+
const handlerResp = await transaction.processEvent(async (transaction, eventRecord) => {
|
|
28
|
+
c_expect(eventRecord.marshalled.NewImage).to.be.deep.equal(record)
|
|
29
|
+
count++
|
|
30
|
+
return Response.SuccessResponse(null)
|
|
31
|
+
})
|
|
32
|
+
// check resp
|
|
33
|
+
c_expect(count).to.be.equals(2)
|
|
34
|
+
c_expect(handlerResp).to.be.an.instanceof(Response)
|
|
35
|
+
if (handlerResp instanceof Response) {
|
|
36
|
+
c_expect(handlerResp?.getBody()).to.be.deep.equal({
|
|
37
|
+
transactionID: 'unknown',
|
|
38
|
+
})
|
|
39
|
+
c_expect(handlerResp.getCode()).to.be.equal(200)
|
|
40
|
+
}
|
|
41
|
+
// ctx
|
|
42
|
+
expect(context.fail).not.toBeCalled()
|
|
43
|
+
expect(context.done).not.toBeCalled()
|
|
44
|
+
expect(context.succeed).not.toBeCalled()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('Simple success - do not decode', async () => {
|
|
48
|
+
const record = { id: '123' }
|
|
49
|
+
const context = observableContext()
|
|
50
|
+
const transaction = new DynamoTransaction(
|
|
51
|
+
emptyDynamoDBEvent({
|
|
52
|
+
Records: [
|
|
53
|
+
{ dynamodb: { NewImage: Utils.ddbMarshall(record) } },
|
|
54
|
+
{ dynamodb: { NewImage: Utils.ddbMarshall(record) } },
|
|
55
|
+
],
|
|
56
|
+
}),
|
|
57
|
+
context,
|
|
58
|
+
{ ...skipCleanTmp }
|
|
59
|
+
)
|
|
60
|
+
let count = 0
|
|
61
|
+
const handlerResp = await transaction.processEvent(async (transaction, eventRecord) => {
|
|
62
|
+
c_expect(eventRecord.dynamodb?.NewImage).to.be.deep.equal(Utils.ddbMarshall(record))
|
|
63
|
+
count++
|
|
64
|
+
return Response.SuccessResponse(null)
|
|
65
|
+
})
|
|
66
|
+
// check resp
|
|
67
|
+
c_expect(count).to.be.equals(2)
|
|
68
|
+
c_expect(handlerResp).to.be.an.instanceof(Response)
|
|
69
|
+
if (handlerResp instanceof Response) {
|
|
70
|
+
c_expect(handlerResp?.getBody()).to.be.deep.equal({
|
|
71
|
+
transactionID: 'unknown',
|
|
72
|
+
})
|
|
73
|
+
c_expect(handlerResp.getCode()).to.be.equal(200)
|
|
74
|
+
}
|
|
75
|
+
// ctx
|
|
76
|
+
expect(context.fail).not.toBeCalled()
|
|
77
|
+
expect(context.done).not.toBeCalled()
|
|
78
|
+
expect(context.succeed).not.toBeCalled()
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
describe('DynamoTransaction failure invocation path', () => {
|
|
83
|
+
test('Simple failure', async () => {
|
|
84
|
+
const record = { id: '123' }
|
|
85
|
+
const context = observableContext()
|
|
86
|
+
const transaction = new DynamoTransaction(
|
|
87
|
+
emptyDynamoDBEvent({
|
|
88
|
+
Records: [
|
|
89
|
+
{ dynamodb: { NewImage: Utils.ddbMarshall(record) } },
|
|
90
|
+
{ eventID: '123', dynamodb: { NewImage: Utils.ddbMarshall(record) } },
|
|
91
|
+
{ dynamodb: { NewImage: Utils.ddbMarshall(record) } },
|
|
92
|
+
],
|
|
93
|
+
}),
|
|
94
|
+
context,
|
|
95
|
+
{ ...skipCleanTmp }
|
|
96
|
+
)
|
|
97
|
+
let count = 0
|
|
98
|
+
let handlerResp: any = null,
|
|
99
|
+
err: any = null
|
|
100
|
+
try {
|
|
101
|
+
handlerResp = await transaction.processEvent(async (transaction, eventRecord) => {
|
|
102
|
+
c_expect(eventRecord.marshalled.NewImage).to.be.deep.equal(record)
|
|
103
|
+
count++
|
|
104
|
+
return count == 1 ? Response.SuccessResponse(null) : Response.BadRequestResponse('Failed!')
|
|
105
|
+
})
|
|
106
|
+
} catch (e) {
|
|
107
|
+
err = e
|
|
108
|
+
}
|
|
109
|
+
// check resp
|
|
110
|
+
c_expect(count).to.be.equals(2)
|
|
111
|
+
c_expect(handlerResp).to.be.null
|
|
112
|
+
c_expect(err).to.be.deep.equal(
|
|
113
|
+
new Error(
|
|
114
|
+
JSON.stringify({
|
|
115
|
+
err: 'Failed!',
|
|
116
|
+
transactionID: 'unknown',
|
|
117
|
+
})
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
// ctx
|
|
121
|
+
expect(context.fail).not.toBeCalled()
|
|
122
|
+
expect(context.done).not.toBeCalled()
|
|
123
|
+
expect(context.succeed).not.toBeCalled()
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test('Simple failure w/ null', async () => {
|
|
127
|
+
const record = { id: '123' }
|
|
128
|
+
const context = observableContext()
|
|
129
|
+
const transaction = new DynamoTransaction(
|
|
130
|
+
emptyDynamoDBEvent({
|
|
131
|
+
Records: [
|
|
132
|
+
{ dynamodb: { NewImage: Utils.ddbMarshall(record) } },
|
|
133
|
+
{ eventID: '123', dynamodb: { NewImage: Utils.ddbMarshall(record) } },
|
|
134
|
+
{ dynamodb: { NewImage: Utils.ddbMarshall(record) } },
|
|
135
|
+
],
|
|
136
|
+
}),
|
|
137
|
+
context,
|
|
138
|
+
{ ...skipCleanTmp }
|
|
139
|
+
)
|
|
140
|
+
let count = 0
|
|
141
|
+
let handlerResp: any = null,
|
|
142
|
+
err: any = null
|
|
143
|
+
try {
|
|
144
|
+
handlerResp = await transaction.processEvent(
|
|
145
|
+
// @ts-ignore
|
|
146
|
+
async (transaction, eventRecord) => {
|
|
147
|
+
c_expect(eventRecord.marshalled.NewImage).to.be.deep.equal(record)
|
|
148
|
+
count++
|
|
149
|
+
return null
|
|
150
|
+
}
|
|
151
|
+
)
|
|
152
|
+
} catch (e) {
|
|
153
|
+
err = e
|
|
154
|
+
}
|
|
155
|
+
// check resp
|
|
156
|
+
c_expect(count).to.be.equals(1)
|
|
157
|
+
c_expect(handlerResp).to.be.null
|
|
158
|
+
c_expect(err).to.be.deep.equal(
|
|
159
|
+
new Error(
|
|
160
|
+
JSON.stringify({
|
|
161
|
+
err: Globals.ErrorResponseInvalidServerResponse,
|
|
162
|
+
rollback: true,
|
|
163
|
+
errCode: Globals.ErrorCode_APIError,
|
|
164
|
+
transactionID: 'unknown',
|
|
165
|
+
})
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
// ctx
|
|
169
|
+
expect(context.fail).not.toBeCalled()
|
|
170
|
+
expect(context.done).not.toBeCalled()
|
|
171
|
+
expect(context.succeed).not.toBeCalled()
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
test('Simple failure - allow failures', async () => {
|
|
175
|
+
const record = { id: '123' }
|
|
176
|
+
const context = observableContext()
|
|
177
|
+
const transaction = new DynamoTransaction(
|
|
178
|
+
emptyDynamoDBEvent({
|
|
179
|
+
Records: [
|
|
180
|
+
{ dynamodb: { NewImage: Utils.ddbMarshall(record) } },
|
|
181
|
+
{ eventID: '123', dynamodb: { NewImage: Utils.ddbMarshall(record) } },
|
|
182
|
+
],
|
|
183
|
+
}),
|
|
184
|
+
context,
|
|
185
|
+
{ ...skipCleanTmp },
|
|
186
|
+
true
|
|
187
|
+
)
|
|
188
|
+
let count = 0
|
|
189
|
+
const handlerResp = await transaction.processEvent(async (transaction, eventRecord) => {
|
|
190
|
+
c_expect(eventRecord.marshalled.NewImage).to.be.deep.equal(record)
|
|
191
|
+
count++
|
|
192
|
+
return count == 1 ? Response.SuccessResponse(null) : Response.BadRequestResponse('Failed!')
|
|
193
|
+
})
|
|
194
|
+
// check resp
|
|
195
|
+
c_expect(count).to.be.equals(2)
|
|
196
|
+
c_expect(handlerResp).to.be.deep.equal({
|
|
197
|
+
batchItemFailures: [{ itemIdentifier: '123' }],
|
|
198
|
+
})
|
|
199
|
+
// ctx
|
|
200
|
+
expect(context.fail).not.toBeCalled()
|
|
201
|
+
expect(context.done).not.toBeCalled()
|
|
202
|
+
expect(context.succeed).not.toBeCalled()
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
test('Simple failure w/ null response - allow failures', async () => {
|
|
206
|
+
const record = { id: '123' }
|
|
207
|
+
const context = observableContext()
|
|
208
|
+
const transaction = new DynamoTransaction(
|
|
209
|
+
emptyDynamoDBEvent({
|
|
210
|
+
Records: [
|
|
211
|
+
{ eventID: '456', dynamodb: { NewImage: Utils.ddbMarshall(record) } },
|
|
212
|
+
{ eventID: '123', dynamodb: { NewImage: Utils.ddbMarshall(record) } },
|
|
213
|
+
],
|
|
214
|
+
}),
|
|
215
|
+
context,
|
|
216
|
+
{ ...skipCleanTmp },
|
|
217
|
+
true
|
|
218
|
+
)
|
|
219
|
+
let count = 0
|
|
220
|
+
const handlerResp = await transaction.processEvent(
|
|
221
|
+
// @ts-ignore
|
|
222
|
+
async (transaction, eventRecord) => {
|
|
223
|
+
c_expect(eventRecord.marshalled.NewImage).to.be.deep.equal(record)
|
|
224
|
+
count++
|
|
225
|
+
return null
|
|
226
|
+
}
|
|
227
|
+
)
|
|
228
|
+
// check resp
|
|
229
|
+
c_expect(count).to.be.equals(2)
|
|
230
|
+
c_expect(handlerResp).to.be.deep.equal({
|
|
231
|
+
batchItemFailures: [{ itemIdentifier: '456' }, { itemIdentifier: '123' }],
|
|
232
|
+
})
|
|
233
|
+
// ctx
|
|
234
|
+
expect(context.fail).not.toBeCalled()
|
|
235
|
+
expect(context.done).not.toBeCalled()
|
|
236
|
+
expect(context.succeed).not.toBeCalled()
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
test('Simple failure no records', async () => {
|
|
240
|
+
const context = observableContext()
|
|
241
|
+
const transaction = new DynamoTransaction(emptyDynamoDBEvent(), context, {})
|
|
242
|
+
let count = 0
|
|
243
|
+
let handlerResp: any = null,
|
|
244
|
+
err: any = null
|
|
245
|
+
try {
|
|
246
|
+
handlerResp = await transaction.processEvent(
|
|
247
|
+
// @ts-ignore
|
|
248
|
+
async () => {
|
|
249
|
+
count++
|
|
250
|
+
return null
|
|
251
|
+
}
|
|
252
|
+
)
|
|
253
|
+
} catch (e) {
|
|
254
|
+
err = e
|
|
255
|
+
}
|
|
256
|
+
// check resp
|
|
257
|
+
c_expect(count).to.be.equals(0)
|
|
258
|
+
c_expect(handlerResp).to.be.null
|
|
259
|
+
c_expect(err).to.be.deep.equal(
|
|
260
|
+
new Error(
|
|
261
|
+
JSON.stringify({
|
|
262
|
+
err: Globals.ErrorResponseNoRecords,
|
|
263
|
+
errCode: Globals.ErrorCode_NoRecords,
|
|
264
|
+
})
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
// ctx
|
|
268
|
+
expect(context.fail).not.toBeCalled()
|
|
269
|
+
expect(context.done).not.toBeCalled()
|
|
270
|
+
expect(context.succeed).not.toBeCalled()
|
|
271
|
+
})
|
|
272
|
+
})
|
|
@@ -154,7 +154,7 @@ function testLogs(isContainer: boolean, provider?: Logger) {
|
|
|
154
154
|
test(`${type} - ${loggerType} Log - Circular reference (Error)`, async () => {
|
|
155
155
|
setContainerFlag(isContainer)
|
|
156
156
|
class SelfRefError extends Error {
|
|
157
|
-
self: SelfRefError
|
|
157
|
+
public self: SelfRefError
|
|
158
158
|
constructor() {
|
|
159
159
|
super()
|
|
160
160
|
this.self = this
|
package/tests/Test.utils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jest } from '@jest/globals'
|
|
2
|
-
import { APIGatewayEvent, Context, SQSEvent } from 'aws-lambda'
|
|
2
|
+
import { APIGatewayEvent, Context, DynamoDBStreamEvent, SQSEvent } from 'aws-lambda'
|
|
3
3
|
import { z } from 'zod'
|
|
4
4
|
|
|
5
5
|
import Transaction from '../src/BaseEvent/Transaction.js'
|
|
@@ -67,6 +67,10 @@ export function emptyQueueEvent(data?: any) {
|
|
|
67
67
|
return <SQSEvent>(<unknown>data || {})
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
export function emptyDynamoDBEvent(data?: any) {
|
|
71
|
+
return <DynamoDBStreamEvent>(<unknown>data || {})
|
|
72
|
+
}
|
|
73
|
+
|
|
70
74
|
export function observableTransaction() {
|
|
71
75
|
const t = new Transaction(emptyEvent(), observableContext())
|
|
72
76
|
t.responseProxy = jest.fn() as any
|