@things-factory/webhook 10.1.29 → 10.1.33
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/dist-server/service/index.d.ts +16 -0
- package/dist-server/service/index.js +28 -0
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/observability/clock-offset.d.ts +88 -0
- package/dist-server/service/observability/clock-offset.js +96 -0
- package/dist-server/service/observability/clock-offset.js.map +1 -0
- package/dist-server/service/observability/trace-context.d.ts +66 -0
- package/dist-server/service/observability/trace-context.js +109 -0
- package/dist-server/service/observability/trace-context.js.map +1 -0
- package/dist-server/service/outbox/append.d.ts +85 -0
- package/dist-server/service/outbox/append.js +46 -0
- package/dist-server/service/outbox/append.js.map +1 -0
- package/dist-server/service/outbox/delivery.d.ts +224 -0
- package/dist-server/service/outbox/delivery.js +337 -0
- package/dist-server/service/outbox/delivery.js.map +1 -0
- package/dist-server/service/outbox/dispatcher.d.ts +46 -0
- package/dist-server/service/outbox/dispatcher.js +109 -0
- package/dist-server/service/outbox/dispatcher.js.map +1 -0
- package/dist-server/service/outbox/page.d.ts +18 -0
- package/dist-server/service/outbox/page.js +14 -0
- package/dist-server/service/outbox/page.js.map +1 -0
- package/dist-server/service/outbox/pump-gate.d.ts +74 -0
- package/dist-server/service/outbox/pump-gate.js +90 -0
- package/dist-server/service/outbox/pump-gate.js.map +1 -0
- package/dist-server/service/outbox/reconciliation.d.ts +114 -0
- package/dist-server/service/outbox/reconciliation.js +115 -0
- package/dist-server/service/outbox/reconciliation.js.map +1 -0
- package/dist-server/service/outbox/requeue-note.d.ts +13 -0
- package/dist-server/service/outbox/requeue-note.js +21 -0
- package/dist-server/service/outbox/requeue-note.js.map +1 -0
- package/dist-server/service/outbox/row-effect.d.ts +68 -0
- package/dist-server/service/outbox/row-effect.js +54 -0
- package/dist-server/service/outbox/row-effect.js.map +1 -0
- package/dist-server/service/outbox/seq-report.d.ts +88 -0
- package/dist-server/service/outbox/seq-report.js +112 -0
- package/dist-server/service/outbox/seq-report.js.map +1 -0
- package/dist-server/service/outbox/settlement.d.ts +63 -0
- package/dist-server/service/outbox/settlement.js +68 -0
- package/dist-server/service/outbox/settlement.js.map +1 -0
- package/dist-server/service/outbox/standing.d.ts +63 -0
- package/dist-server/service/outbox/standing.js +65 -0
- package/dist-server/service/outbox/standing.js.map +1 -0
- package/dist-server/service/outbox/store.d.ts +52 -0
- package/dist-server/service/outbox/store.js +3 -0
- package/dist-server/service/outbox/store.js.map +1 -0
- package/dist-server/service/outbox/webhook-store.d.ts +17 -0
- package/dist-server/service/outbox/webhook-store.js +62 -0
- package/dist-server/service/outbox/webhook-store.js.map +1 -0
- package/dist-server/service/outbox/writing-transaction.d.ts +33 -0
- package/dist-server/service/outbox/writing-transaction.js +40 -0
- package/dist-server/service/outbox/writing-transaction.js.map +1 -0
- package/dist-server/service/webhook-destination/webhook-destination.d.ts +3 -0
- package/dist-server/service/webhook-destination/webhook-destination.js +15 -0
- package/dist-server/service/webhook-destination/webhook-destination.js.map +1 -1
- package/dist-server/service/webhook-outbox-event/webhook-outbox-event.d.ts +3 -0
- package/dist-server/service/webhook-outbox-event/webhook-outbox-event.js +22 -0
- package/dist-server/service/webhook-outbox-event/webhook-outbox-event.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/tests/outbox-append.test.ts +106 -0
- package/tests/outbox-pump-gate.test.ts +65 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/webhook",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.33",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"browser": "dist-client/index.js",
|
|
6
6
|
"things-factory": true,
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@operato/graphql": "^10.0.0",
|
|
31
31
|
"@operato/i18n": "^10.0.0",
|
|
32
|
-
"@operato/ops-contract": "^0.9.
|
|
33
|
-
"@things-factory/auth-base": "^10.1.
|
|
32
|
+
"@operato/ops-contract": "^0.9.30",
|
|
33
|
+
"@things-factory/auth-base": "^10.1.33",
|
|
34
34
|
"@things-factory/env": "^10.1.20",
|
|
35
|
-
"@things-factory/shell": "^10.1.
|
|
36
|
-
"@things-factory/task-lease": "^10.1.
|
|
35
|
+
"@things-factory/shell": "^10.1.33",
|
|
36
|
+
"@things-factory/task-lease": "^10.1.33"
|
|
37
37
|
},
|
|
38
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "7fb7b6587846b5ff6ee7a4983652d9561b4d6b2c"
|
|
39
39
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The back row of the outbox: the order of the two writes, and what is refused before either.
|
|
3
|
+
*
|
|
4
|
+
* The number and the row have to commit together. A caller that passed the default manager burned 200
|
|
5
|
+
* numbers at one site before anybody noticed, so the manager is now asked for in the type as well as
|
|
6
|
+
* checked - and the check is what these tests can reach.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { appendOutbox, openTransaction, type NewOutboxRow } from '../server/service/outbox/append.js'
|
|
10
|
+
|
|
11
|
+
const inTransaction = { queryRunner: { isTransactionActive: true } }
|
|
12
|
+
|
|
13
|
+
function store() {
|
|
14
|
+
const inserted: NewOutboxRow<any>[][] = []
|
|
15
|
+
const allocated: number[] = []
|
|
16
|
+
let last = 100
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
inserted,
|
|
20
|
+
allocated,
|
|
21
|
+
async allocate(count: number) {
|
|
22
|
+
allocated.push(count)
|
|
23
|
+
last += count
|
|
24
|
+
|
|
25
|
+
return last - count + 1
|
|
26
|
+
},
|
|
27
|
+
async insert(rows: readonly NewOutboxRow<any>[]) {
|
|
28
|
+
inserted.push([...rows])
|
|
29
|
+
|
|
30
|
+
return rows.map((row, index) => ({ ...row, id: `r${index}` }))
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const tx = () => openTransaction(inTransaction as any)
|
|
36
|
+
|
|
37
|
+
describe('appendOutbox', () => {
|
|
38
|
+
it('will not take a manager that is not in a transaction - the number would commit alone', () => {
|
|
39
|
+
expect(() => openTransaction({} as any)).toThrow(/transaction/)
|
|
40
|
+
expect(() => openTransaction({ queryRunner: {} } as any)).toThrow(/transaction/)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('numbers a batch in one run, in the order given', async () => {
|
|
44
|
+
const one = store()
|
|
45
|
+
|
|
46
|
+
await appendOutbox(tx(), one, [{ record: { a: 1 } }, { record: { a: 2 } }, { record: { a: 3 } }])
|
|
47
|
+
|
|
48
|
+
expect(one.allocated).toEqual([3])
|
|
49
|
+
expect(one.inserted[0].map(row => row.seq)).toEqual([101, 102, 103])
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('takes no number at all for an empty batch', async () => {
|
|
53
|
+
const one = store()
|
|
54
|
+
|
|
55
|
+
expect(await appendOutbox(tx(), one, [])).toEqual([])
|
|
56
|
+
expect(one.allocated).toEqual([])
|
|
57
|
+
expect(one.inserted).toEqual([])
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('gives every row an id the receiver can dedupe on, and keeps one the caller named', async () => {
|
|
61
|
+
const one = store()
|
|
62
|
+
|
|
63
|
+
await appendOutbox(tx(), one, [{ record: { a: 1 } }, { record: { a: 2 }, eventId: 'urn:uuid:mine' }])
|
|
64
|
+
|
|
65
|
+
const [first, second] = one.inserted[0]
|
|
66
|
+
|
|
67
|
+
expect(first.eventId).toMatch(/^urn:uuid:/)
|
|
68
|
+
expect(second.eventId).toBe('urn:uuid:mine')
|
|
69
|
+
expect(first.eventId).not.toBe(second.eventId)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('stamps one record time for the batch, and keeps each event time', async () => {
|
|
73
|
+
const one = store()
|
|
74
|
+
const happened = new Date('2026-09-01T00:00:00.000Z')
|
|
75
|
+
|
|
76
|
+
await appendOutbox(tx(), one, [{ record: { a: 1 }, eventTime: happened }, { record: { a: 2 } }])
|
|
77
|
+
|
|
78
|
+
const [first, second] = one.inserted[0]
|
|
79
|
+
|
|
80
|
+
expect(first.eventTime).toBe(happened)
|
|
81
|
+
expect(second.eventTime).toEqual(second.recordTime)
|
|
82
|
+
expect(first.recordTime).toEqual(second.recordTime)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('what is refused before anything is written', () => {
|
|
86
|
+
it('an empty payload - it would be delivered as a fact', async () => {
|
|
87
|
+
const one = store()
|
|
88
|
+
|
|
89
|
+
await expect(appendOutbox(tx(), one, [{ record: undefined as any }])).rejects.toThrow(/record is required/)
|
|
90
|
+
expect(one.allocated).toEqual([])
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('★ a record the product says has a broken identity, and no number is taken', async () => {
|
|
94
|
+
const one = store()
|
|
95
|
+
const check = (record: any) => (record.epc ? [] : ['epc is missing'])
|
|
96
|
+
|
|
97
|
+
await expect(appendOutbox(tx(), one, [{ record: { epc: 'x' } }, { record: {} }], { check })).rejects.toThrow(
|
|
98
|
+
/identity is not valid - epc is missing/
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
/* The whole batch is refused before the counter moves - a rolled-back raise is a burned number. */
|
|
102
|
+
expect(one.allocated).toEqual([])
|
|
103
|
+
expect(one.inserted).toEqual([])
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
})
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether this installation can sign, and whose key a domain gets.
|
|
3
|
+
*
|
|
4
|
+
* This is the judgement that decides whether anything is delivered at all, so it takes the
|
|
5
|
+
* environment as an argument - read from the process it would be out of a test's reach, and the way
|
|
6
|
+
* it fails is silence: rows pile up and nothing errors.
|
|
7
|
+
*
|
|
8
|
+
* Moved from operato-plant with the names lifted out (ADR-0078 decision 2 ②, decision 5 as amended):
|
|
9
|
+
* the module owns the shape of a per-domain variable, the product owns the stems.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { domainSecretName, outboxCanSign, outboxSecretFor } from '../server/service/outbox/pump-gate.js'
|
|
13
|
+
|
|
14
|
+
const names = { common: 'MES_OUTBOX_SECRET', domainPrefix: 'MES_OUTBOX_SECRET__' }
|
|
15
|
+
|
|
16
|
+
describe('a domain becomes a variable name', () => {
|
|
17
|
+
it('upper case, and anything else becomes an underscore', () => {
|
|
18
|
+
expect(domainSecretName('MES_OUTBOX_SECRET__', 'miratek-gunsan')).toBe('MES_OUTBOX_SECRET__MIRATEK_GUNSAN')
|
|
19
|
+
expect(domainSecretName('MES_OUTBOX_SECRET__', 'miratek.gunsan')).toBe('MES_OUTBOX_SECRET__MIRATEK_GUNSAN')
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
describe('can this installation sign', () => {
|
|
24
|
+
it('no - nothing named holds a value', () => {
|
|
25
|
+
expect(outboxCanSign({}, names)).toBe(false)
|
|
26
|
+
expect(outboxCanSign({ SOMETHING_ELSE: 'x' }, names)).toBe(false)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('★ no - a key that is only whitespace signs, so it does not count as a key', () => {
|
|
30
|
+
expect(outboxCanSign({ MES_OUTBOX_SECRET: ' ' }, names)).toBe(false)
|
|
31
|
+
expect(outboxCanSign({ MES_OUTBOX_SECRET__MIRATEK_GUNSAN: '\t' }, names)).toBe(false)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('yes - the common key, a domain key, or one declared in configuration', () => {
|
|
35
|
+
expect(outboxCanSign({ MES_OUTBOX_SECRET: 'k' }, names)).toBe(true)
|
|
36
|
+
expect(outboxCanSign({ MES_OUTBOX_SECRET__MIRATEK_GUNSAN: 'k' }, names)).toBe(true)
|
|
37
|
+
expect(outboxCanSign({}, names, 'k')).toBe(true)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('★ counts only the names this product declared - another product\'s keys are not ours', () => {
|
|
41
|
+
const warehouse = { WMS_OUTBOX_SECRET: 'k', WMS_OUTBOX_SECRET__MIRATEK_GUNSAN: 'k' }
|
|
42
|
+
|
|
43
|
+
expect(outboxCanSign(warehouse, names)).toBe(false)
|
|
44
|
+
expect(outboxCanSign(warehouse, { common: 'WMS_OUTBOX_SECRET', domainPrefix: 'WMS_OUTBOX_SECRET__' })).toBe(true)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('whose key this domain signs with', () => {
|
|
49
|
+
it('★ its own comes before the common one', () => {
|
|
50
|
+
const env = { MES_OUTBOX_SECRET: 'common', MES_OUTBOX_SECRET__MIRATEK_GUNSAN: 'mine' }
|
|
51
|
+
|
|
52
|
+
expect(outboxSecretFor(env, names, 'miratek-gunsan')).toBe('mine')
|
|
53
|
+
expect(outboxSecretFor(env, names, 'somewhere-else')).toBe('common')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('★ absent is undefined, never an empty string - an empty key signs', () => {
|
|
57
|
+
expect(outboxSecretFor({}, names, 'miratek-gunsan')).toBeUndefined()
|
|
58
|
+
expect(outboxSecretFor({ MES_OUTBOX_SECRET: ' ' }, names, 'miratek-gunsan')).toBeUndefined()
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('a key declared in configuration is the last resort', () => {
|
|
62
|
+
expect(outboxSecretFor({}, names, 'miratek-gunsan', 'declared')).toBe('declared')
|
|
63
|
+
expect(outboxSecretFor({ MES_OUTBOX_SECRET: 'common' }, names, 'miratek-gunsan', 'declared')).toBe('common')
|
|
64
|
+
})
|
|
65
|
+
})
|