@teleporthq/teleport-plugin-next-workflows 0.45.4 → 0.45.6
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/__tests__/account-delete-password-check.test.ts +39 -0
- package/__tests__/analytics-outcome-visit-ids.test.ts +583 -0
- package/__tests__/automations-bus.test.ts +354 -0
- package/__tests__/public-step-guards.test.ts +26 -0
- package/dist/cjs/account-delete-route-generator.d.ts.map +1 -1
- package/dist/cjs/account-delete-route-generator.js +17 -8
- package/dist/cjs/account-delete-route-generator.js.map +1 -1
- package/dist/cjs/api-route-generator.d.ts +2 -0
- package/dist/cjs/api-route-generator.d.ts.map +1 -1
- package/dist/cjs/api-route-generator.js +43 -1
- package/dist/cjs/api-route-generator.js.map +1 -1
- package/dist/cjs/automations/dispatcher-source.d.ts.map +1 -1
- package/dist/cjs/automations/dispatcher-source.js +13 -5
- package/dist/cjs/automations/dispatcher-source.js.map +1 -1
- package/dist/cjs/executor-generator.d.ts.map +1 -1
- package/dist/cjs/executor-generator.js +4 -0
- package/dist/cjs/executor-generator.js.map +1 -1
- package/dist/cjs/public-step-guards.d.ts +2 -2
- package/dist/cjs/public-step-guards.d.ts.map +1 -1
- package/dist/cjs/public-step-guards.js +11 -8
- package/dist/cjs/public-step-guards.js.map +1 -1
- package/dist/cjs/publish-refusals.js +1 -1
- package/dist/cjs/publish-refusals.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/account-delete-route-generator.d.ts.map +1 -1
- package/dist/esm/account-delete-route-generator.js +17 -8
- package/dist/esm/account-delete-route-generator.js.map +1 -1
- package/dist/esm/api-route-generator.d.ts +2 -0
- package/dist/esm/api-route-generator.d.ts.map +1 -1
- package/dist/esm/api-route-generator.js +44 -2
- package/dist/esm/api-route-generator.js.map +1 -1
- package/dist/esm/automations/dispatcher-source.d.ts.map +1 -1
- package/dist/esm/automations/dispatcher-source.js +13 -5
- package/dist/esm/automations/dispatcher-source.js.map +1 -1
- package/dist/esm/executor-generator.d.ts.map +1 -1
- package/dist/esm/executor-generator.js +4 -0
- package/dist/esm/executor-generator.js.map +1 -1
- package/dist/esm/public-step-guards.d.ts +2 -2
- package/dist/esm/public-step-guards.d.ts.map +1 -1
- package/dist/esm/public-step-guards.js +11 -8
- package/dist/esm/public-step-guards.js.map +1 -1
- package/dist/esm/publish-refusals.js +1 -1
- package/dist/esm/publish-refusals.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/account-delete-route-generator.ts +17 -8
- package/src/api-route-generator.ts +52 -2
- package/src/automations/dispatcher-source.ts +13 -5
- package/src/executor-generator.ts +4 -0
- package/src/public-step-guards.ts +14 -8
- package/src/publish-refusals.ts +1 -1
|
@@ -2887,6 +2887,360 @@ describe('in-app event bus — the mail automations send', () => {
|
|
|
2887
2887
|
})
|
|
2888
2888
|
})
|
|
2889
2889
|
|
|
2890
|
+
describe('in-app event bus — a run that sent nothing', () => {
|
|
2891
|
+
const mail = (id: string, to: unknown, label: string, stepNumber: number, extra: Row = {}) => ({
|
|
2892
|
+
id,
|
|
2893
|
+
type: 'email-resend',
|
|
2894
|
+
config: {
|
|
2895
|
+
apiKey: 'k',
|
|
2896
|
+
from: 'shop@example.com',
|
|
2897
|
+
to,
|
|
2898
|
+
subject: 'Order paid',
|
|
2899
|
+
body: '<p>Order paid</p>',
|
|
2900
|
+
...extra,
|
|
2901
|
+
},
|
|
2902
|
+
stepNumber,
|
|
2903
|
+
label,
|
|
2904
|
+
})
|
|
2905
|
+
const gate = (stepNumber: number, left: unknown, rightValue: string) => ({
|
|
2906
|
+
id: 'gate',
|
|
2907
|
+
type: 'general-if-statement',
|
|
2908
|
+
config: { leftValue: left, operator: 'equals', rightValue },
|
|
2909
|
+
stepNumber,
|
|
2910
|
+
label: 'Check',
|
|
2911
|
+
})
|
|
2912
|
+
const OWNER = 'owner@shop.test'
|
|
2913
|
+
const orderNumber = ref('t', ['order', 'number'])
|
|
2914
|
+
const userEmail = ref('t', ['user', 'email'])
|
|
2915
|
+
const onPaid = (id: string, nodes: Row[], edges: Row[]) => ({
|
|
2916
|
+
id,
|
|
2917
|
+
name: id,
|
|
2918
|
+
trigger: trigger('order-paid'),
|
|
2919
|
+
nodes,
|
|
2920
|
+
edges,
|
|
2921
|
+
})
|
|
2922
|
+
const toGate = { id: 'e1', source: 't', target: 'gate' }
|
|
2923
|
+
const whenTrue = { id: 'e2', source: 'gate', target: 'send', sourceHandle: 'true' }
|
|
2924
|
+
const guarded = (id: string, rightValue: string) =>
|
|
2925
|
+
onPaid(
|
|
2926
|
+
id,
|
|
2927
|
+
[gate(1, orderNumber, rightValue), mail('send', OWNER, 'Send Email to Owner', 2)],
|
|
2928
|
+
[toGate, whenTrue]
|
|
2929
|
+
)
|
|
2930
|
+
const afterPost = { id: 'e0', source: 't', target: 'hold' }
|
|
2931
|
+
const holdToGate = { id: 'e1', source: 'hold', target: 'gate' }
|
|
2932
|
+
const parks = {
|
|
2933
|
+
id: 'wait',
|
|
2934
|
+
type: 'general-wait',
|
|
2935
|
+
config: { amount: 1, unit: 'days' },
|
|
2936
|
+
label: 'Wait a day',
|
|
2937
|
+
}
|
|
2938
|
+
const WORKFLOWS: Record<string, Row> = {
|
|
2939
|
+
'wf-skipped': guarded('wf-skipped', 'NOT-THIS-ORDER'),
|
|
2940
|
+
'wf-sent': guarded('wf-sent', 'ORD-1042'),
|
|
2941
|
+
'wf-no-recipient': onPaid(
|
|
2942
|
+
'wf-no-recipient',
|
|
2943
|
+
[mail('send', ref('t', ['order', 'nobody']), 'Send', 1)],
|
|
2944
|
+
[{ id: 'e1', source: 't', target: 'send' }]
|
|
2945
|
+
),
|
|
2946
|
+
'wf-failed-and-went-on': onPaid(
|
|
2947
|
+
'wf-failed-and-went-on',
|
|
2948
|
+
[mail('send', ref('t', ['order', 'nobody']), 'Send', 1, { continueOnError: true })],
|
|
2949
|
+
[{ id: 'e1', source: 't', target: 'send' }]
|
|
2950
|
+
),
|
|
2951
|
+
'wf-one-of-two': onPaid(
|
|
2952
|
+
'wf-one-of-two',
|
|
2953
|
+
[
|
|
2954
|
+
gate(1, orderNumber, 'NOT-THIS-ORDER'),
|
|
2955
|
+
mail('send', OWNER, 'Skipped', 2),
|
|
2956
|
+
mail('other', OWNER, 'Sent', 3),
|
|
2957
|
+
],
|
|
2958
|
+
[toGate, whenTrue, { id: 'e3', source: 'gate', target: 'other', sourceHandle: 'false' }]
|
|
2959
|
+
),
|
|
2960
|
+
'wf-slow-skipped': onPaid(
|
|
2961
|
+
'wf-slow-skipped',
|
|
2962
|
+
[
|
|
2963
|
+
post('hold', '/hold', 1),
|
|
2964
|
+
gate(2, orderNumber, 'NOT-THIS-ORDER'),
|
|
2965
|
+
mail('send', OWNER, 'Send Email to Owner', 3),
|
|
2966
|
+
],
|
|
2967
|
+
[afterPost, holdToGate, whenTrue]
|
|
2968
|
+
),
|
|
2969
|
+
'wf-wait-then-skipped': {
|
|
2970
|
+
id: 'wf-wait-then-skipped',
|
|
2971
|
+
name: 'wf-wait-then-skipped',
|
|
2972
|
+
trigger: trigger('user-signed-up'),
|
|
2973
|
+
nodes: [
|
|
2974
|
+
{ ...parks, stepNumber: 1 },
|
|
2975
|
+
post('hold', '/hold', 2),
|
|
2976
|
+
gate(3, userEmail, 'nobody@example.com'),
|
|
2977
|
+
mail('send', OWNER, 'Send Email to Owner', 4),
|
|
2978
|
+
],
|
|
2979
|
+
edges: [
|
|
2980
|
+
{ id: 'e0', source: 't', target: 'wait' },
|
|
2981
|
+
{ id: 'e1', source: 'wait', target: 'hold' },
|
|
2982
|
+
holdToGate,
|
|
2983
|
+
whenTrue,
|
|
2984
|
+
],
|
|
2985
|
+
},
|
|
2986
|
+
'wf-mail-then-wait': {
|
|
2987
|
+
id: 'wf-mail-then-wait',
|
|
2988
|
+
name: 'wf-mail-then-wait',
|
|
2989
|
+
trigger: trigger('user-signed-up'),
|
|
2990
|
+
nodes: [
|
|
2991
|
+
mail('send', OWNER, 'Welcome', 1),
|
|
2992
|
+
{ ...parks, stepNumber: 2 },
|
|
2993
|
+
post('after', '/after', 3),
|
|
2994
|
+
],
|
|
2995
|
+
edges: [
|
|
2996
|
+
{ id: 'e0', source: 't', target: 'send' },
|
|
2997
|
+
{ id: 'e1', source: 'send', target: 'wait' },
|
|
2998
|
+
{ id: 'e2', source: 'wait', target: 'after' },
|
|
2999
|
+
],
|
|
3000
|
+
},
|
|
3001
|
+
}
|
|
3002
|
+
const NOTE = JSON.stringify({ sentNothing: ['Send Email to Owner'] })
|
|
3003
|
+
let files: Map<string, string>
|
|
3004
|
+
|
|
3005
|
+
beforeAll(async () => {
|
|
3006
|
+
files = sources(await generate(WORKFLOWS))
|
|
3007
|
+
})
|
|
3008
|
+
|
|
3009
|
+
const paidOrder = (workflowId: string) => {
|
|
3010
|
+
const db = new FakeDatabase()
|
|
3011
|
+
seedOrderPaid(db)
|
|
3012
|
+
db.automations = [automationRow(workflowId, 'order-paid')]
|
|
3013
|
+
return db
|
|
3014
|
+
}
|
|
3015
|
+
const signup = (workflowId: string) => {
|
|
3016
|
+
const db = new FakeDatabase()
|
|
3017
|
+
db.users.push({ id: 'user-1', email: 'noah@example.com', name: 'Noah', created_at: ago(1000) })
|
|
3018
|
+
db.automations = [automationRow(workflowId, 'user-signed-up')]
|
|
3019
|
+
db.events.push(
|
|
3020
|
+
eventRow({
|
|
3021
|
+
id: 'evt-signup',
|
|
3022
|
+
event_type: 'user-signed-up',
|
|
3023
|
+
entity_type: 'user',
|
|
3024
|
+
entity_id: 'user-1',
|
|
3025
|
+
actor: 'noah@example.com',
|
|
3026
|
+
actor_type: 'customer',
|
|
3027
|
+
})
|
|
3028
|
+
)
|
|
3029
|
+
return db
|
|
3030
|
+
}
|
|
3031
|
+
const dispatch = (db: FakeDatabase) =>
|
|
3032
|
+
withEcho(() =>
|
|
3033
|
+
silenced(() => (createApp(files, db)('lib/automations/event-bus.js') as Bus).dispatch({}))
|
|
3034
|
+
)
|
|
3035
|
+
/** A dispatch waiting 20 ms for a handler held at its first fetch, released afterwards. */
|
|
3036
|
+
const dispatchHeldOpen = async (db: FakeDatabase) => {
|
|
3037
|
+
const held: Array<() => void> = []
|
|
3038
|
+
const scope = globalThis as Record<string, unknown>
|
|
3039
|
+
const previousFetch = scope.fetch
|
|
3040
|
+
scope.fetch = () =>
|
|
3041
|
+
new Promise((resolve) => {
|
|
3042
|
+
held.push(() =>
|
|
3043
|
+
resolve({
|
|
3044
|
+
ok: true,
|
|
3045
|
+
status: 200,
|
|
3046
|
+
headers: new Map([['content-type', 'application/json']]),
|
|
3047
|
+
json: async () => ({}),
|
|
3048
|
+
text: async () => '{}',
|
|
3049
|
+
})
|
|
3050
|
+
)
|
|
3051
|
+
})
|
|
3052
|
+
try {
|
|
3053
|
+
await withEnv(ENV, () =>
|
|
3054
|
+
silenced(async () => {
|
|
3055
|
+
await (createApp(files, db)('lib/automations/event-bus.js') as Bus).dispatch({
|
|
3056
|
+
handlerTimeoutMs: 20,
|
|
3057
|
+
})
|
|
3058
|
+
expect(held).toHaveLength(1)
|
|
3059
|
+
held[0]()
|
|
3060
|
+
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
3061
|
+
})
|
|
3062
|
+
)
|
|
3063
|
+
} finally {
|
|
3064
|
+
scope.fetch = previousFetch
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
it('records the send steps a condition skipped, and the run stays delivered', async () => {
|
|
3069
|
+
const db = paidOrder('wf-skipped')
|
|
3070
|
+
|
|
3071
|
+
const result = await dispatch(db)
|
|
3072
|
+
|
|
3073
|
+
expect(result).toEqual({ claimed: 1, delivered: 1, failed: 0, resumed: 0 })
|
|
3074
|
+
expect(db.mailSent).toEqual([])
|
|
3075
|
+
expect(db.delivery('wf-skipped')).toMatchObject({ status: 'delivered', error: NOTE })
|
|
3076
|
+
expect(db.events[0]).toMatchObject({ status: 'delivered', attempts: 1 })
|
|
3077
|
+
})
|
|
3078
|
+
|
|
3079
|
+
it('records nothing when a send step sent, or when the automation has none', async () => {
|
|
3080
|
+
const sent = paidOrder('wf-sent')
|
|
3081
|
+
await dispatch(sent)
|
|
3082
|
+
expect(sent.mailSent).toHaveLength(1)
|
|
3083
|
+
expect(sent.delivery('wf-sent')).toMatchObject({ status: 'delivered', error: null })
|
|
3084
|
+
|
|
3085
|
+
const oneOfTwo = paidOrder('wf-one-of-two')
|
|
3086
|
+
await dispatch(oneOfTwo)
|
|
3087
|
+
expect(oneOfTwo.mailSent).toHaveLength(1)
|
|
3088
|
+
expect(oneOfTwo.delivery('wf-one-of-two')).toMatchObject({ status: 'delivered', error: null })
|
|
3089
|
+
})
|
|
3090
|
+
|
|
3091
|
+
it('counts a send that failed and went on as unsent', async () => {
|
|
3092
|
+
const db = paidOrder('wf-failed-and-went-on')
|
|
3093
|
+
await dispatch(db)
|
|
3094
|
+
expect(db.delivery('wf-failed-and-went-on')).toMatchObject({
|
|
3095
|
+
status: 'delivered',
|
|
3096
|
+
error: JSON.stringify({ sentNothing: ['Send'] }),
|
|
3097
|
+
})
|
|
3098
|
+
})
|
|
3099
|
+
|
|
3100
|
+
it('leaves a send that stops the run failing as before, with its own error', async () => {
|
|
3101
|
+
const db = paidOrder('wf-no-recipient')
|
|
3102
|
+
const result = await dispatch(db)
|
|
3103
|
+
expect(result).toEqual({ claimed: 1, delivered: 0, failed: 1, resumed: 0 })
|
|
3104
|
+
expect(db.mailSent).toEqual([])
|
|
3105
|
+
expect(db.delivery('wf-no-recipient')).toMatchObject({
|
|
3106
|
+
status: 'failed',
|
|
3107
|
+
error: 'No recipient',
|
|
3108
|
+
})
|
|
3109
|
+
})
|
|
3110
|
+
|
|
3111
|
+
it('records it for a run that ends after the dispatcher stopped waiting', async () => {
|
|
3112
|
+
const db = paidOrder('wf-slow-skipped')
|
|
3113
|
+
await dispatchHeldOpen(db)
|
|
3114
|
+
expect(db.delivery('wf-slow-skipped')).toMatchObject({ status: 'delivered', error: NOTE })
|
|
3115
|
+
})
|
|
3116
|
+
|
|
3117
|
+
it('records it for a run resumed after its Wait', async () => {
|
|
3118
|
+
const db = signup('wf-wait-then-skipped')
|
|
3119
|
+
await dispatch(db)
|
|
3120
|
+
expect(db.delivery('wf-wait-then-skipped')).toMatchObject({ status: 'waiting', error: null })
|
|
3121
|
+
|
|
3122
|
+
db.waits[0].run_at = ago(1000)
|
|
3123
|
+
await dispatch(db)
|
|
3124
|
+
|
|
3125
|
+
expect(db.delivery('wf-wait-then-skipped')).toMatchObject({
|
|
3126
|
+
status: 'delivered',
|
|
3127
|
+
error: NOTE,
|
|
3128
|
+
})
|
|
3129
|
+
})
|
|
3130
|
+
|
|
3131
|
+
it('records it for a resume that ends after the dispatcher stopped waiting', async () => {
|
|
3132
|
+
const db = signup('wf-wait-then-skipped')
|
|
3133
|
+
await dispatch(db)
|
|
3134
|
+
db.waits[0].run_at = ago(1000)
|
|
3135
|
+
await dispatchHeldOpen(db)
|
|
3136
|
+
expect(db.delivery('wf-wait-then-skipped')).toMatchObject({
|
|
3137
|
+
status: 'delivered',
|
|
3138
|
+
error: NOTE,
|
|
3139
|
+
})
|
|
3140
|
+
})
|
|
3141
|
+
|
|
3142
|
+
it('counts a mail sent before the Wait when the run resumes after it', async () => {
|
|
3143
|
+
const db = signup('wf-mail-then-wait')
|
|
3144
|
+
await dispatch(db)
|
|
3145
|
+
db.waits[0].run_at = ago(1000)
|
|
3146
|
+
await dispatch(db)
|
|
3147
|
+
expect(db.mailSent).toHaveLength(1)
|
|
3148
|
+
expect(db.delivery('wf-mail-then-wait')).toMatchObject({ status: 'delivered', error: null })
|
|
3149
|
+
})
|
|
3150
|
+
|
|
3151
|
+
it('records it for a test event, whose row stays a test', async () => {
|
|
3152
|
+
const db = paidOrder('wf-skipped')
|
|
3153
|
+
db.events = []
|
|
3154
|
+
|
|
3155
|
+
const result = await withEcho(() =>
|
|
3156
|
+
silenced(() => {
|
|
3157
|
+
const { runTest } = createApp(files, db)('lib/automations/dispatcher.js') as {
|
|
3158
|
+
runTest(input: Row, options: Row): Promise<Totals>
|
|
3159
|
+
}
|
|
3160
|
+
return runTest(
|
|
3161
|
+
{
|
|
3162
|
+
workflowId: 'wf-skipped',
|
|
3163
|
+
event: { order: { number: 'ORD-TEST', customer: { email: 'test@example.com' } } },
|
|
3164
|
+
},
|
|
3165
|
+
{}
|
|
3166
|
+
)
|
|
3167
|
+
})
|
|
3168
|
+
)
|
|
3169
|
+
|
|
3170
|
+
expect(result).toEqual({ claimed: 1, delivered: 1, failed: 0, resumed: 0 })
|
|
3171
|
+
expect(db.delivery('wf-skipped')).toMatchObject({ status: 'test', error: NOTE })
|
|
3172
|
+
})
|
|
3173
|
+
|
|
3174
|
+
describe('which steps a run can report', () => {
|
|
3175
|
+
const sendStepsOf = async (workflow: Row): Promise<Row[]> => {
|
|
3176
|
+
const { generateHandlerFunction } = await import('../src/api-route-generator')
|
|
3177
|
+
const source = generateHandlerFunction(JSON.parse(JSON.stringify(workflow)))
|
|
3178
|
+
return JSON.parse(/var SEND_STEPS = (.*);/.exec(source)![1])
|
|
3179
|
+
}
|
|
3180
|
+
const sms = {
|
|
3181
|
+
id: 'text',
|
|
3182
|
+
type: 'sms-twilio',
|
|
3183
|
+
config: { to: '+15550142', message: 'Paid' },
|
|
3184
|
+
stepNumber: 3,
|
|
3185
|
+
label: 'Text the owner',
|
|
3186
|
+
}
|
|
3187
|
+
const email = mail('send', OWNER, 'Send Email to Owner', 2)
|
|
3188
|
+
const steps = [
|
|
3189
|
+
{ id: 'send', label: 'Send Email to Owner' },
|
|
3190
|
+
{ id: 'text', label: 'Text the owner' },
|
|
3191
|
+
]
|
|
3192
|
+
|
|
3193
|
+
it('lists the email and SMS steps, and nothing else', async () => {
|
|
3194
|
+
expect(await sendStepsOf(onPaid('w', [post('p', '/p', 1), email, sms], []))).toEqual(steps)
|
|
3195
|
+
expect(await sendStepsOf(onPaid('w', [post('p', '/p', 1)], []))).toEqual([])
|
|
3196
|
+
})
|
|
3197
|
+
|
|
3198
|
+
it('leaves out the steps of the error branch, which a run that ends well never reaches', async () => {
|
|
3199
|
+
const onError = mail('alarm', OWNER, 'Tell me it broke', 4)
|
|
3200
|
+
const workflow = {
|
|
3201
|
+
...onPaid(
|
|
3202
|
+
'w',
|
|
3203
|
+
[
|
|
3204
|
+
email,
|
|
3205
|
+
{ id: 'err', type: 'event-workflow-error', config: {}, stepNumber: 3, label: 'Error' },
|
|
3206
|
+
onError,
|
|
3207
|
+
],
|
|
3208
|
+
[{ id: 'e1', source: 'err', target: 'alarm' }]
|
|
3209
|
+
),
|
|
3210
|
+
errorHandler: { nodeId: 'err' },
|
|
3211
|
+
}
|
|
3212
|
+
expect(await sendStepsOf(workflow)).toEqual([{ id: 'send', label: 'Send Email to Owner' }])
|
|
3213
|
+
})
|
|
3214
|
+
|
|
3215
|
+
it('reports nothing when a step it cannot see may have sent', async () => {
|
|
3216
|
+
const customNode = {
|
|
3217
|
+
id: 'custom',
|
|
3218
|
+
type: 'general-custom-node',
|
|
3219
|
+
config: {},
|
|
3220
|
+
stepNumber: 1,
|
|
3221
|
+
label: 'Custom',
|
|
3222
|
+
}
|
|
3223
|
+
const parallel = (config: Row) => ({
|
|
3224
|
+
id: 'both',
|
|
3225
|
+
type: 'general-parallel',
|
|
3226
|
+
config,
|
|
3227
|
+
stepNumber: 1,
|
|
3228
|
+
label: 'Both',
|
|
3229
|
+
})
|
|
3230
|
+
expect(await sendStepsOf(onPaid('w', [customNode, email], []))).toEqual([])
|
|
3231
|
+
expect(await sendStepsOf(onPaid('w', [parallel({ waitForAll: false }), email], []))).toEqual(
|
|
3232
|
+
[]
|
|
3233
|
+
)
|
|
3234
|
+
expect(await sendStepsOf(onPaid('w', [parallel({ waitForAll: true }), email], []))).toEqual([
|
|
3235
|
+
{ id: 'send', label: 'Send Email to Owner' },
|
|
3236
|
+
])
|
|
3237
|
+
expect(await sendStepsOf(onPaid('w', [parallel({}), email], []))).toEqual([
|
|
3238
|
+
{ id: 'send', label: 'Send Email to Owner' },
|
|
3239
|
+
])
|
|
3240
|
+
})
|
|
3241
|
+
})
|
|
3242
|
+
})
|
|
3243
|
+
|
|
2890
3244
|
describe('in-app event bus — emitting and the dispatch route', () => {
|
|
2891
3245
|
let files: Map<string, string>
|
|
2892
3246
|
|
|
@@ -3,6 +3,7 @@ import adminFileWorkflows from './fixtures/admin-file-workflows.json'
|
|
|
3
3
|
import {
|
|
4
4
|
PUBLIC_STEP_REFUSAL_CODE,
|
|
5
5
|
PublicStepRefusalError,
|
|
6
|
+
assertNoPublicRestrictedSteps,
|
|
6
7
|
findPublicRestrictedSteps,
|
|
7
8
|
} from '../src/public-step-guards'
|
|
8
9
|
|
|
@@ -234,6 +235,31 @@ describe('restricted steps on a public route', () => {
|
|
|
234
235
|
expect(error).toBeInstanceOf(PublicStepRefusalError)
|
|
235
236
|
})
|
|
236
237
|
|
|
238
|
+
it('on a site without sign-in, says to enable authentication before any role can be asked', () => {
|
|
239
|
+
const workflows = {
|
|
240
|
+
workflows: { w1: pageWorkflow([node('n1', 'file-storage-delete', 'Delete Upload')]) },
|
|
241
|
+
}
|
|
242
|
+
const refusedWith = (authentication: unknown): string => {
|
|
243
|
+
try {
|
|
244
|
+
assertNoPublicRestrictedSteps(workflows, authentication as never)
|
|
245
|
+
} catch (error) {
|
|
246
|
+
return shownSentence(error as Error)
|
|
247
|
+
}
|
|
248
|
+
throw new Error('not refused')
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
for (const withoutSignIn of [refusedWith(null), refusedWith({ enabled: false, roles: [] })]) {
|
|
252
|
+
expect(withoutSignIn).toContain('"Delete Upload"')
|
|
253
|
+
expect(withoutSignIn).toContain('has no sign-in')
|
|
254
|
+
expect(withoutSignIn).toContain('enable authentication')
|
|
255
|
+
expect(withoutSignIn).not.toContain('let only the role')
|
|
256
|
+
expect(withoutSignIn.length).toBeLessThanOrEqual(400)
|
|
257
|
+
}
|
|
258
|
+
const withSignIn = refusedWith({ enabled: true, roles: ['admin', 'user'] })
|
|
259
|
+
expect(withSignIn).toContain('let only the role "admin" start it')
|
|
260
|
+
expect(withSignIn).not.toContain('has no sign-in')
|
|
261
|
+
})
|
|
262
|
+
|
|
237
263
|
it('stays one short sentence for many steps', () => {
|
|
238
264
|
const nodes = Array.from({ length: 12 }, (_, i) =>
|
|
239
265
|
node(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-delete-route-generator.d.ts","sourceRoot":"","sources":["../../src/account-delete-route-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAA;AAcxE,MAAM,WAAW,yBAAyB;IAExC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAI3B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IAGtB,kBAAkB,CAAC,EAAE,2BAA2B,CAAA;IAEhD,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAG5B,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAID,eAAO,MAAM,8BAA8B,+CAAiC,CAAA;AAE5E,eAAO,MAAM,0BAA0B,aAAa,yBAAyB,KAAQ,
|
|
1
|
+
{"version":3,"file":"account-delete-route-generator.d.ts","sourceRoot":"","sources":["../../src/account-delete-route-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAA;AAcxE,MAAM,WAAW,yBAAyB;IAExC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAI3B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IAGtB,kBAAkB,CAAC,EAAE,2BAA2B,CAAA;IAEhD,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAG5B,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAID,eAAO,MAAM,8BAA8B,+CAAiC,CAAA;AAE5E,eAAO,MAAM,0BAA0B,aAAa,yBAAyB,KAAQ,MAicpF,CAAA"}
|
|
@@ -275,19 +275,28 @@ function readRequestPassword(req) {
|
|
|
275
275
|
return String(body.password);
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
// The sign-in check (utils/auth/hash-password.js
|
|
279
|
-
// the SHA-256 of the password and
|
|
280
|
-
//
|
|
278
|
+
// The sign-in check (utils/auth/hash-password.js checkPassword): bcrypt over
|
|
279
|
+
// the SHA-256 of the password and a pepper, the project's own
|
|
280
|
+
// (AUTH_PEPPER_SECRET) first, then the one every earlier hash used
|
|
281
|
+
// (AUTH_PEPPER). An account without a stored password (a social sign-in)
|
|
282
|
+
// matches no password.
|
|
281
283
|
function passwordMatches(password, storedHash) {
|
|
282
284
|
if (!storedHash || password.length > 1000) { return false; }
|
|
283
285
|
var bcrypt = require('bcryptjs');
|
|
284
286
|
var crypto = require('crypto');
|
|
285
|
-
var
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
287
|
+
var secret = process.env.AUTH_PEPPER_SECRET;
|
|
288
|
+
var peppers = typeof secret === 'string' && secret && secret.indexOf('teleporthq.secrets.') !== 0
|
|
289
|
+
? [secret, process.env.AUTH_PEPPER || '']
|
|
290
|
+
: [process.env.AUTH_PEPPER || ''];
|
|
291
|
+
for (var i = 0; i < peppers.length; i++) {
|
|
292
|
+
var prepared = crypto.createHash('sha256').update(password + peppers[i]).digest('base64');
|
|
293
|
+
try {
|
|
294
|
+
if (bcrypt.compareSync(prepared, String(storedHash))) { return true; }
|
|
295
|
+
} catch (e) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
290
298
|
}
|
|
299
|
+
return false;
|
|
291
300
|
}
|
|
292
301
|
|
|
293
302
|
${(0, transactional_email_code_1.generateFillTemplateFn)()}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-delete-route-generator.js","sourceRoot":"","sources":["../../src/account-delete-route-generator.ts"],"names":[],"mappings":";;;AA2BA,iEAA0D;AAC1D,qDAAuD;AACvD,uEAAoF;AACpF,yEAOmC;AACnC,qDAAsF;AA0BtF,+EAA+E;AAC/E,oFAAoF;AACvE,QAAA,8BAA8B,GAAG,yDAA8B,CAAA;AAErE,MAAM,0BAA0B,GAAG,CAAC,UAAqC,EAAE,EAAU,EAAE;IAC5F,MAAM,cAAc,GAAG,OAAO,CAAC,kBAAkB,IAAI,OAAO,CAAA;IAC5D,MAAM,QAAQ,GACZ,OAAO,CAAC,aAAa,IAAI,oDAAyB,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;QAC3E,CAAC,CAAC,OAAO,CAAC,aAAa;QACvB,CAAC,CAAC,IAAI,CAAA;IACV,MAAM,mBAAmB,GACvB,OAAO,CAAC,mBAAmB,IAAI,yCAAyC,CAAA;IAC1E,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,KAAK,IAAI,CAAA;IAExD,OAAO,GAAG,IAAA,qCAAoB,GAAE;EAChC,IAAA,kEAAwC,GAAE;2BACjB,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;oBACrC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;gCAC1B,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC;yBAC1C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;qBAC5B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;0BAClC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;gCAChC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,kBAAkB,IAAI,EAAE,CAAC;wBACxD,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,YAAY,IAAI,+BAA+B,CAAC;0BACrE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC;0BAC3C,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,kBAAkB,IAAI,EAAE,CAAC;EACxE,IAAA,qDAA0B,EAAC,UAAU,CAAC;;;;;;;;;;;KAWnC,sCAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyHxB,8BAAY,CAAC,+BAA+B,EAAE
|
|
1
|
+
{"version":3,"file":"account-delete-route-generator.js","sourceRoot":"","sources":["../../src/account-delete-route-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -65,6 +65,8 @@ export declare const generateWebhookWorkflowAPIRoute: (workflow: UIDLWorkflow, c
|
|
|
65
65
|
* nothing and resolves `{ waiting: true, wouldWait }`; a Wait with no step
|
|
66
66
|
* after it stores nothing and the run ends there). A run that fails rejects,
|
|
67
67
|
* after the error-handler branch ran, so the dispatcher records the failure.
|
|
68
|
+
* A run that ends with its email and SMS steps all unsent resolves with their
|
|
69
|
+
* names under `sentNothing`, so the History can say so instead of Done.
|
|
68
70
|
*/
|
|
69
71
|
export declare const generateHandlerFunction: (workflow: UIDLWorkflow, customNodes?: Record<string, UIDLCustomWorkflowNode>) => string;
|
|
70
72
|
export declare const getWebhookRouteFileName: (workflow: UIDLWorkflow) => string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-route-generator.d.ts","sourceRoot":"","sources":["../../src/api-route-generator.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EAGb,iBAAiB,EACjB,sBAAsB,EACtB,sBAAsB,EACvB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAA;
|
|
1
|
+
{"version":3,"file":"api-route-generator.d.ts","sourceRoot":"","sources":["../../src/api-route-generator.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EAGb,iBAAiB,EACjB,sBAAsB,EACtB,sBAAsB,EACvB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAA;AAuEvE,MAAM,WAAW,yBAAyB;IACxC,0EAA0E;IAC1E,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;;;OAIG;IACH,KAAK,CAAC,EAAE,YAAY,CAAA;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAA;CACrD;AAED,OAAO,EAAE,qBAAqB,EAAE,CAAA;AAEhC,wEAAwE;AACxE,eAAO,MAAM,wBAAwB;;;CAAiC,CAAA;AAEtE,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,4CAA4C,CAAA;AA+O7E,eAAO,MAAM,6BAA6B,YAC/B,eAAe,iBACT,MAAM,eACR,sBAAsB,YAC1B,yBAAyB,KACjC,MAyXF,CAAA;AAED,eAAO,MAAM,kBAAkB,YAAa,eAAe,KAAG,OAE7D,CAAA;AAED,eAAO,MAAM,sCAAsC,YACxC,eAAe,iBACT,MAAM,eACR,sBAAsB,YAC1B,yBAAyB,KACjC,MAycF,CAAA;AA6ID;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,aA4D5B,CAAA;AAEF,qDAAqD;AACrD,MAAM,WAAW,0BAA0B;IACzC,kEAAkE;IAClE,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAqBD,eAAO,MAAM,oBAAoB,aACrB,YAAY,gBACR,OAAO,MAAM,EAAE,sBAAsB,CAAC,YAC3C,0BAA0B,KAClC,MA0HF,CAAA;AAED,eAAO,MAAM,mBAAmB,eAClB,MAAM,aACP,MAAM,iBACF,MAAM,KACpB,MA4BF,CAAA;AAED,eAAO,MAAM,oBAAoB,aAAc,YAAY,KAAG,MAa7D,CAAA;AAsTD,uHAAuH;AACvH,eAAO,MAAM,uBAAuB,kBAAmB,iBAAiB,KAAG,OAES,CAAA;AAEpF,eAAO,MAAM,+BAA+B,aAChC,YAAY,gBACR,OAAO,MAAM,EAAE,sBAAsB,CAAC,YAC3C,0BAA0B,KAClC,MAkOF,CAAA;AAkED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,uBAAuB,aACxB,YAAY,gBACR,OAAO,MAAM,EAAE,sBAAsB,CAAC,KACnD,MA8KF,CAAA;AAED,eAAO,MAAM,uBAAuB,aAAc,YAAY,KAAG,MAiBhE,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,cAC1B,aAAa,GAAG,SAAS,cACxB,MAAM,KACjB,MAAM,GAAG,IAYX,CAAA;AAED,eAAO,MAAM,mBAAmB,aAAc,YAAY,KAAG,MAAM,EAqBlE,CAAA"}
|
|
@@ -8,6 +8,7 @@ const nodes_1 = require("./nodes");
|
|
|
8
8
|
const types_1 = require("./nodes/types");
|
|
9
9
|
const general_wait_1 = require("./nodes/general/general-wait");
|
|
10
10
|
const visitor_request_1 = require("./nodes/general/visitor-request");
|
|
11
|
+
const message_trust_1 = require("./nodes/email/message-trust");
|
|
11
12
|
const outbound_message_guard_1 = require("./nodes/email/outbound-message-guard");
|
|
12
13
|
const ai_provider_utils_1 = require("./nodes/ai/ai-provider-utils");
|
|
13
14
|
const automations_scope_1 = require("./automations/automations-scope");
|
|
@@ -1916,6 +1917,29 @@ const generateErrorHandlerRunBlock = (failureLabel) => ` if (ERROR_HANDLER_NO
|
|
|
1916
1917
|
console.error('${failureLabel}', innerErr);
|
|
1917
1918
|
}
|
|
1918
1919
|
}`;
|
|
1920
|
+
/**
|
|
1921
|
+
* The email and SMS steps a run of this automation can report as not having
|
|
1922
|
+
* sent. None when it could not tell: a custom node may send by itself, and a
|
|
1923
|
+
* parallel branch the run does not wait for has no result when the run ends.
|
|
1924
|
+
* Steps on the error branch do not count: a run that ends well never reaches them.
|
|
1925
|
+
*/
|
|
1926
|
+
const reportableSendSteps = (workflow, errorHandlerNodes) => {
|
|
1927
|
+
const nodes = workflow.nodes || [];
|
|
1928
|
+
const couldNotTell = nodes.some((node) => {
|
|
1929
|
+
var _a;
|
|
1930
|
+
return node.type === 'general-custom-node' ||
|
|
1931
|
+
(node.type === 'general-parallel' &&
|
|
1932
|
+
((_a = node.config) === null || _a === void 0 ? void 0 : _a.waitForAll) !== undefined &&
|
|
1933
|
+
node.config.waitForAll !== true);
|
|
1934
|
+
});
|
|
1935
|
+
if (couldNotTell) {
|
|
1936
|
+
return [];
|
|
1937
|
+
}
|
|
1938
|
+
const errorBranch = new Set((errorHandlerNodes || []).map((node) => node.id));
|
|
1939
|
+
return nodes
|
|
1940
|
+
.filter((node) => (0, visitor_request_1.isOutboundMessageNodeType)(node.type) && !errorBranch.has(node.id))
|
|
1941
|
+
.map((node) => ({ id: node.id, label: (0, message_trust_1.stepNameOf)(node) }));
|
|
1942
|
+
};
|
|
1919
1943
|
/**
|
|
1920
1944
|
* An automation's handler module (`lib/automations/handlers/<file>.js`, see
|
|
1921
1945
|
* automations/handler-module-generator.ts): the webhook route's node loop as
|
|
@@ -1935,6 +1959,8 @@ const generateErrorHandlerRunBlock = (failureLabel) => ` if (ERROR_HANDLER_NO
|
|
|
1935
1959
|
* nothing and resolves `{ waiting: true, wouldWait }`; a Wait with no step
|
|
1936
1960
|
* after it stores nothing and the run ends there). A run that fails rejects,
|
|
1937
1961
|
* after the error-handler branch ran, so the dispatcher records the failure.
|
|
1962
|
+
* A run that ends with its email and SMS steps all unsent resolves with their
|
|
1963
|
+
* names under `sentNothing`, so the History can say so instead of Done.
|
|
1938
1964
|
*/
|
|
1939
1965
|
const generateHandlerFunction = (workflow, customNodes) => {
|
|
1940
1966
|
var _a;
|
|
@@ -1992,6 +2018,8 @@ const WORKFLOW_CONFIG = ${workflowConfig};
|
|
|
1992
2018
|
|
|
1993
2019
|
var ERROR_HANDLER_NODES = ${errorHandlerNodesJson};
|
|
1994
2020
|
|
|
2021
|
+
var SEND_STEPS = ${JSON.stringify(reportableSendSteps(workflow, errorHandlerNodes))};
|
|
2022
|
+
|
|
1995
2023
|
const nodeHandlers = {
|
|
1996
2024
|
${nodeHandlersEntries}
|
|
1997
2025
|
};
|
|
@@ -2033,6 +2061,17 @@ ${executionLoop}
|
|
|
2033
2061
|
return { waiting: false };
|
|
2034
2062
|
}
|
|
2035
2063
|
|
|
2064
|
+
// The names of the send steps when the run has some and none of them sent: a
|
|
2065
|
+
// step a condition skipped has no result, one that failed and went on says so.
|
|
2066
|
+
function unsentSendSteps(context) {
|
|
2067
|
+
if (SEND_STEPS.length === 0) return null;
|
|
2068
|
+
var sent = SEND_STEPS.some(function(step) {
|
|
2069
|
+
var result = context[step.id];
|
|
2070
|
+
return !!result && !utils.isFatalNodeResult(result);
|
|
2071
|
+
});
|
|
2072
|
+
return sent ? null : SEND_STEPS.map(function(step) { return step.label; });
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2036
2075
|
async function run(event, context, resumeFrom) {
|
|
2037
2076
|
context = context && typeof context === 'object' ? context : {};
|
|
2038
2077
|
// Shared fire-and-forget queue — drained before the run settles.
|
|
@@ -2065,13 +2104,16 @@ async function run(event, context, resumeFrom) {
|
|
|
2065
2104
|
sourceRef: WORKFLOW_ID,
|
|
2066
2105
|
orderId: trigger.order && trigger.order.id ? String(trigger.order.id) : null,
|
|
2067
2106
|
};
|
|
2107
|
+
var outcome;
|
|
2068
2108
|
try {
|
|
2069
|
-
|
|
2109
|
+
outcome = await runNodes(context, resumeFrom || null);
|
|
2070
2110
|
} catch (error) {
|
|
2071
2111
|
${generateErrorHandlerRunBlock('Automation error handler failed:')}
|
|
2072
2112
|
await utils.settlePendingNodePromises(context);
|
|
2073
2113
|
throw error;
|
|
2074
2114
|
}
|
|
2115
|
+
var unsent = outcome.waiting ? null : unsentSendSteps(context);
|
|
2116
|
+
return unsent ? Object.assign({}, outcome, { sentNothing: unsent }) : outcome;
|
|
2075
2117
|
}
|
|
2076
2118
|
|
|
2077
2119
|
module.exports.run = run;
|