-
Notifications
You must be signed in to change notification settings - Fork 5
/
index.js
180 lines (166 loc) · 6.21 KB
/
index.js
1
2
3
4
5
6
7
8
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
let { join } = require('path')
let { existsSync, readFileSync } = require('fs')
let { updater } = require('@architect/utils')
let { prompt } = require('enquirer')
let colors = require('ansi-colors')
let update = updater('Invoker')
let mock = require('./event-mocks')
let lastInvoke
module.exports = {
sandbox: {
start: async ({ inventory: { inv }, invoke }) => {
start()
update.status(`Event invoker started, select an event to invoke by pressing 'i'`)
let { cwd, preferences } = inv._project
let jsonMocks = join(cwd, 'sandbox-invoke-mocks.json')
let jsMocks = join(cwd, 'sandbox-invoke-mocks.js')
let pragmas = [ 'customLambdas', 'events', 'queues', 'scheduled', 'tables-streams' ]
let prefs = preferences?.sandbox?.invoker
if (prefs) {
if (Array.isArray(prefs)) pragmas = prefs
else if (typeof prefs === 'string') pragmas = [ prefs ]
else throw Error('Invalid @architect/plugin-lambda-invoker plugin preferences')
}
process.stdin.on('data', async function eventInvokeListener (input) {
// Build out the available event list each time to prevent caching
let events = {}
pragmas.forEach(pragma => {
if (inv[pragma]) inv[pragma].forEach(({ name }) => {
events[`@${pragma} ${name}`] = { pragma, name }
})
})
// Add a cancel option should one desire
events.cancel = ''
let lastEventName
if (lastInvoke) {
lastEventName = `Last invoke: @${lastInvoke.pragma} ${lastInvoke.name} (${lastInvoke.mockName})`
events[lastEventName] = lastInvoke
}
start()
input = String(input)
// Reset Enquirer's styles
let options = {
prefix: colors.white(colors.symbols?.question ?? '?'),
styles: {
em: colors.cyan, // Clear underlines
danger: colors.red,
strong: colors.white,
}
}
if (input === 'i') {
if (Object.keys(events).length === 1) {
let none = 'No Lambdas found to invoke'
if (pragmas.length) update.status(none, `Using the following pragmas: @${pragmas.join(', @')}`)
else update.status(none)
return
}
let userPayload = {}
let mockName = 'empty'
let pragma, name, mocks, skipSelection
// Load invocation mocks
if (existsSync(jsonMocks)) {
mocks = JSON.parse(readFileSync(jsonMocks))
}
else if (existsSync(jsMocks)) {
// Make sure changes to mocks are always reflected
delete require.cache[require.resolve(jsMocks)]
// eslint-disable-next-line
mocks = require(jsMocks)
}
try {
let { lambda } = await prompt({
type: 'select',
name: 'lambda',
numbered: true,
message: 'Which event do you want to invoke?',
hint: '\nYou can use numbers to change your selection',
choices: Object.keys(events),
}, options)
if (lambda === 'cancel') return start()
else if (lambda === lastEventName) {
skipSelection = true
var event = events[lastEventName]
mockName = event.mockName
}
else {
var event = events[lambda]
}
pragma = event.pragma
name = event.name
// Set up non-cached user payload from last event
if (lambda === lastEventName) {
userPayload = mocks[pragma][name][mockName] || {}
}
}
catch (err) {
update.status('Canceled invoke')
return start()
}
// Present options for mocks (if any)
let mockable = ![ 'scheduled', 'tables-streams' ].includes(pragma)
if (mocks?.[pragma]?.[name] && mockable && !skipSelection) {
let selection = await prompt({
type: 'select',
name: 'mock',
numbered: true,
message: 'Which mock do you want to invoke?',
choices: [ ...Object.keys(mocks[pragma][name]), 'empty' ],
}, options)
mockName = selection.mock
userPayload = mocks[pragma][name][mockName] || {}
}
lastInvoke = { pragma, name, mockName, userPayload }
let payload
/**/ if (pragma === 'events') payload = mock.events(userPayload)
else if (pragma === 'queues') payload = mock.queues(userPayload)
else if (pragma === 'scheduled') payload = mock.scheduled()
else if (pragma === 'customLambdas') payload = mock.customLambdas(userPayload)
else if (pragma === 'tables-streams') {
let { eventName } = await prompt({
type: 'select',
name: 'eventName',
message: 'Which kind of Dynamo Stream event do you want to invoke?',
choices: [ 'INSERT', 'MODIFY', 'REMOVE' ]
})
payload = mock.tablesStreams(eventName)
}
else {
if (!Object.keys(userPayload).length) {
update.warning('Warning: real AWS event sources generally do not emit empty payloads')
}
payload = userPayload
}
// Wrap it up and invoke!
let msg = `Invoking @${pragma} ${name}`
msg += ` with ${mockName === 'empty' ? 'empty' : `'${mockName}'`} payload`
update.status(msg)
await invoke({ pragma, name, payload })
start()
}
})
},
end: async () => {
// Only remove our listener; removing Enquirer causes funky behavior
process.stdin.rawListeners('data').forEach(fn => {
if (fn.name === 'eventInvokeListener') {
process.stdin.removeListener('data', fn)
}
})
end()
}
}
}
// Necessary per Enquirer #326
function start () {
if (process.stdin.isTTY) {
process.stdin.setRawMode(true)
process.stdin.setEncoding('utf8')
process.stdin.resume()
}
}
// Super important to pause stdin, or Sandbox will hang forever in tests
function end () {
if (process.stdin.isTTY) {
process.stdin.pause()
}
}