@contember/engine-actions 2.2.0-alpha.2 → 2.2.0-alpha.4

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.
Files changed (200) hide show
  1. package/CLAUDE.md +58 -0
  2. package/dist/development/index.cjs +19 -7
  3. package/dist/development/index.cjs.map +1 -1
  4. package/dist/development/index.js +19 -7
  5. package/dist/development/index.js.map +1 -1
  6. package/dist/development/src/ActionsExecutionContainerHookFactory.cjs +5 -2
  7. package/dist/development/src/ActionsExecutionContainerHookFactory.cjs.map +1 -1
  8. package/dist/development/src/ActionsExecutionContainerHookFactory.js +5 -2
  9. package/dist/development/src/ActionsExecutionContainerHookFactory.js.map +1 -1
  10. package/dist/development/src/ActionsMetrics.cjs +98 -0
  11. package/dist/development/src/ActionsMetrics.cjs.map +1 -0
  12. package/dist/development/src/ActionsMetrics.js +98 -0
  13. package/dist/development/src/ActionsMetrics.js.map +1 -0
  14. package/dist/development/src/dispatch/EventDispatcher.cjs +27 -9
  15. package/dist/development/src/dispatch/EventDispatcher.cjs.map +1 -1
  16. package/dist/development/src/dispatch/EventDispatcher.js +27 -9
  17. package/dist/development/src/dispatch/EventDispatcher.js.map +1 -1
  18. package/dist/development/src/dispatch/EventsRepository.cjs +49 -20
  19. package/dist/development/src/dispatch/EventsRepository.cjs.map +1 -1
  20. package/dist/development/src/dispatch/EventsRepository.js +49 -20
  21. package/dist/development/src/dispatch/EventsRepository.js.map +1 -1
  22. package/dist/development/src/dispatch/ProjectDispatcher.cjs +21 -13
  23. package/dist/development/src/dispatch/ProjectDispatcher.cjs.map +1 -1
  24. package/dist/development/src/dispatch/ProjectDispatcher.js +21 -13
  25. package/dist/development/src/dispatch/ProjectDispatcher.js.map +1 -1
  26. package/dist/development/src/dispatch/WebhookTargetHandler.cjs +29 -2
  27. package/dist/development/src/dispatch/WebhookTargetHandler.cjs.map +1 -1
  28. package/dist/development/src/dispatch/WebhookTargetHandler.js +29 -2
  29. package/dist/development/src/dispatch/WebhookTargetHandler.js.map +1 -1
  30. package/dist/development/src/graphql/resolvers/ResolversFactory.cjs +1 -1
  31. package/dist/development/src/graphql/resolvers/ResolversFactory.cjs.map +1 -1
  32. package/dist/development/src/graphql/resolvers/ResolversFactory.js +1 -1
  33. package/dist/development/src/graphql/resolvers/ResolversFactory.js.map +1 -1
  34. package/dist/development/src/graphql/resolvers/mutation/SetVariablesMutationResolver.cjs +1 -1
  35. package/dist/development/src/graphql/resolvers/mutation/SetVariablesMutationResolver.cjs.map +1 -1
  36. package/dist/development/src/graphql/resolvers/mutation/SetVariablesMutationResolver.js +1 -1
  37. package/dist/development/src/graphql/resolvers/mutation/SetVariablesMutationResolver.js.map +1 -1
  38. package/dist/development/src/graphql/resolvers/query/EventsQueryResolver.cjs +1 -1
  39. package/dist/development/src/graphql/resolvers/query/EventsQueryResolver.cjs.map +1 -1
  40. package/dist/development/src/graphql/resolvers/query/EventsQueryResolver.js +1 -1
  41. package/dist/development/src/graphql/resolvers/query/EventsQueryResolver.js.map +1 -1
  42. package/dist/development/src/graphql/resolvers/query/VariablesQueryResolver.cjs +1 -4
  43. package/dist/development/src/graphql/resolvers/query/VariablesQueryResolver.cjs.map +1 -1
  44. package/dist/development/src/graphql/resolvers/query/VariablesQueryResolver.js +1 -4
  45. package/dist/development/src/graphql/resolvers/query/VariablesQueryResolver.js.map +1 -1
  46. package/dist/development/src/graphql/schema/actions.graphql.cjs +20 -8
  47. package/dist/development/src/graphql/schema/actions.graphql.cjs.map +1 -1
  48. package/dist/development/src/graphql/schema/actions.graphql.js +20 -8
  49. package/dist/development/src/graphql/schema/actions.graphql.js.map +1 -1
  50. package/dist/development/src/model/VariablesManager.cjs +54 -5
  51. package/dist/development/src/model/VariablesManager.cjs.map +1 -1
  52. package/dist/development/src/model/VariablesManager.js +54 -5
  53. package/dist/development/src/model/VariablesManager.js.map +1 -1
  54. package/dist/development/src/triggers/TriggerPayloadPersister.cjs +3 -1
  55. package/dist/development/src/triggers/TriggerPayloadPersister.cjs.map +1 -1
  56. package/dist/development/src/triggers/TriggerPayloadPersister.js +3 -1
  57. package/dist/development/src/triggers/TriggerPayloadPersister.js.map +1 -1
  58. package/dist/production/index.cjs +19 -7
  59. package/dist/production/index.cjs.map +1 -1
  60. package/dist/production/index.js +19 -7
  61. package/dist/production/index.js.map +1 -1
  62. package/dist/production/src/ActionsExecutionContainerHookFactory.cjs +5 -2
  63. package/dist/production/src/ActionsExecutionContainerHookFactory.cjs.map +1 -1
  64. package/dist/production/src/ActionsExecutionContainerHookFactory.js +5 -2
  65. package/dist/production/src/ActionsExecutionContainerHookFactory.js.map +1 -1
  66. package/dist/production/src/ActionsMetrics.cjs +98 -0
  67. package/dist/production/src/ActionsMetrics.cjs.map +1 -0
  68. package/dist/production/src/ActionsMetrics.js +98 -0
  69. package/dist/production/src/ActionsMetrics.js.map +1 -0
  70. package/dist/production/src/dispatch/EventDispatcher.cjs +27 -9
  71. package/dist/production/src/dispatch/EventDispatcher.cjs.map +1 -1
  72. package/dist/production/src/dispatch/EventDispatcher.js +27 -9
  73. package/dist/production/src/dispatch/EventDispatcher.js.map +1 -1
  74. package/dist/production/src/dispatch/EventsRepository.cjs +49 -20
  75. package/dist/production/src/dispatch/EventsRepository.cjs.map +1 -1
  76. package/dist/production/src/dispatch/EventsRepository.js +49 -20
  77. package/dist/production/src/dispatch/EventsRepository.js.map +1 -1
  78. package/dist/production/src/dispatch/ProjectDispatcher.cjs +21 -13
  79. package/dist/production/src/dispatch/ProjectDispatcher.cjs.map +1 -1
  80. package/dist/production/src/dispatch/ProjectDispatcher.js +21 -13
  81. package/dist/production/src/dispatch/ProjectDispatcher.js.map +1 -1
  82. package/dist/production/src/dispatch/WebhookTargetHandler.cjs +29 -2
  83. package/dist/production/src/dispatch/WebhookTargetHandler.cjs.map +1 -1
  84. package/dist/production/src/dispatch/WebhookTargetHandler.js +29 -2
  85. package/dist/production/src/dispatch/WebhookTargetHandler.js.map +1 -1
  86. package/dist/production/src/graphql/resolvers/ResolversFactory.cjs +1 -1
  87. package/dist/production/src/graphql/resolvers/ResolversFactory.cjs.map +1 -1
  88. package/dist/production/src/graphql/resolvers/ResolversFactory.js +1 -1
  89. package/dist/production/src/graphql/resolvers/ResolversFactory.js.map +1 -1
  90. package/dist/production/src/graphql/resolvers/mutation/SetVariablesMutationResolver.cjs +1 -1
  91. package/dist/production/src/graphql/resolvers/mutation/SetVariablesMutationResolver.cjs.map +1 -1
  92. package/dist/production/src/graphql/resolvers/mutation/SetVariablesMutationResolver.js +1 -1
  93. package/dist/production/src/graphql/resolvers/mutation/SetVariablesMutationResolver.js.map +1 -1
  94. package/dist/production/src/graphql/resolvers/query/EventsQueryResolver.cjs +1 -1
  95. package/dist/production/src/graphql/resolvers/query/EventsQueryResolver.cjs.map +1 -1
  96. package/dist/production/src/graphql/resolvers/query/EventsQueryResolver.js +1 -1
  97. package/dist/production/src/graphql/resolvers/query/EventsQueryResolver.js.map +1 -1
  98. package/dist/production/src/graphql/resolvers/query/VariablesQueryResolver.cjs +1 -4
  99. package/dist/production/src/graphql/resolvers/query/VariablesQueryResolver.cjs.map +1 -1
  100. package/dist/production/src/graphql/resolvers/query/VariablesQueryResolver.js +1 -4
  101. package/dist/production/src/graphql/resolvers/query/VariablesQueryResolver.js.map +1 -1
  102. package/dist/production/src/graphql/schema/actions.graphql.cjs +20 -8
  103. package/dist/production/src/graphql/schema/actions.graphql.cjs.map +1 -1
  104. package/dist/production/src/graphql/schema/actions.graphql.js +20 -8
  105. package/dist/production/src/graphql/schema/actions.graphql.js.map +1 -1
  106. package/dist/production/src/model/VariablesManager.cjs +54 -5
  107. package/dist/production/src/model/VariablesManager.cjs.map +1 -1
  108. package/dist/production/src/model/VariablesManager.js +54 -5
  109. package/dist/production/src/model/VariablesManager.js.map +1 -1
  110. package/dist/production/src/triggers/TriggerPayloadPersister.cjs +3 -1
  111. package/dist/production/src/triggers/TriggerPayloadPersister.cjs.map +1 -1
  112. package/dist/production/src/triggers/TriggerPayloadPersister.js +3 -1
  113. package/dist/production/src/triggers/TriggerPayloadPersister.js.map +1 -1
  114. package/dist/tests/cases/unit/actionsMetrics.test.d.ts +2 -0
  115. package/dist/tests/cases/unit/actionsMetrics.test.d.ts.map +1 -0
  116. package/dist/tests/cases/unit/eventsRepository.test.d.ts +2 -0
  117. package/dist/tests/cases/unit/eventsRepository.test.d.ts.map +1 -0
  118. package/dist/tests/cases/unit/variablesManager.test.d.ts +2 -0
  119. package/dist/tests/cases/unit/variablesManager.test.d.ts.map +1 -0
  120. package/dist/tests/src/event.d.ts +4 -0
  121. package/dist/tests/src/event.d.ts.map +1 -0
  122. package/dist/tests/src/gql.d.ts.map +1 -1
  123. package/dist/tests/src/uuid.d.ts.map +1 -1
  124. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  125. package/dist/types/ActionsExecutionContainerHookFactory.d.ts +3 -1
  126. package/dist/types/ActionsExecutionContainerHookFactory.d.ts.map +1 -1
  127. package/dist/types/ActionsMetrics.d.ts +59 -0
  128. package/dist/types/ActionsMetrics.d.ts.map +1 -0
  129. package/dist/types/ListenerStoreProvider.d.ts.map +1 -1
  130. package/dist/types/authorization/ActionsPermissionsFactory.d.ts.map +1 -1
  131. package/dist/types/dispatch/DispatchWorkerSupervisor.d.ts.map +1 -1
  132. package/dist/types/dispatch/EventDispatcher.d.ts +12 -3
  133. package/dist/types/dispatch/EventDispatcher.d.ts.map +1 -1
  134. package/dist/types/dispatch/EventsRepository.d.ts +14 -2
  135. package/dist/types/dispatch/EventsRepository.d.ts.map +1 -1
  136. package/dist/types/dispatch/LazyDispatchWorker.d.ts.map +1 -1
  137. package/dist/types/dispatch/ProjectDispatcher.d.ts +5 -2
  138. package/dist/types/dispatch/ProjectDispatcher.d.ts.map +1 -1
  139. package/dist/types/dispatch/TargetHandlerResolver.d.ts.map +1 -1
  140. package/dist/types/dispatch/WebhookFetcher.d.ts.map +1 -1
  141. package/dist/types/dispatch/WebhookTargetHandler.d.ts.map +1 -1
  142. package/dist/types/graphql/http/ActionsApiMiddlewareFactory.d.ts.map +1 -1
  143. package/dist/types/graphql/http/ActionsContextResolver.d.ts.map +1 -1
  144. package/dist/types/graphql/http/ActionsGraphQLHandlerFactory.d.ts.map +1 -1
  145. package/dist/types/graphql/http/ActionsWebsocketControllerFactory.d.ts.map +1 -1
  146. package/dist/types/graphql/resolvers/ResolversFactory.d.ts.map +1 -1
  147. package/dist/types/graphql/resolvers/mutation/ProcessBatchMutationResolver.d.ts.map +1 -1
  148. package/dist/types/graphql/resolvers/mutation/RetryEventMutationResolver.d.ts.map +1 -1
  149. package/dist/types/graphql/resolvers/mutation/SetVariablesMutationResolver.d.ts.map +1 -1
  150. package/dist/types/graphql/resolvers/mutation/StopEventMutationResolver.d.ts.map +1 -1
  151. package/dist/types/graphql/resolvers/query/EventsQueryResolver.d.ts.map +1 -1
  152. package/dist/types/graphql/resolvers/query/VariablesQueryResolver.d.ts +1 -4
  153. package/dist/types/graphql/resolvers/query/VariablesQueryResolver.d.ts.map +1 -1
  154. package/dist/types/graphql/schema/actions.graphql.d.ts.map +1 -1
  155. package/dist/types/graphql/schema/index.d.ts +27 -17
  156. package/dist/types/graphql/schema/index.d.ts.map +1 -1
  157. package/dist/types/index.d.ts +6 -0
  158. package/dist/types/index.d.ts.map +1 -1
  159. package/dist/types/model/EventByIdQuery.d.ts.map +1 -1
  160. package/dist/types/model/EventsInProcessingQuery.d.ts.map +1 -1
  161. package/dist/types/model/EventsToProcessQuery.d.ts.map +1 -1
  162. package/dist/types/model/FailedEventsQuery.d.ts.map +1 -1
  163. package/dist/types/model/SetVariableCommand.d.ts.map +1 -1
  164. package/dist/types/model/VariablesManager.d.ts +16 -3
  165. package/dist/types/model/VariablesManager.d.ts.map +1 -1
  166. package/dist/types/model/VariablesQuery.d.ts.map +1 -1
  167. package/dist/types/triggers/TriggerHandler.d.ts.map +1 -1
  168. package/dist/types/triggers/TriggerIndirectChangesFetcher.d.ts.map +1 -1
  169. package/dist/types/triggers/TriggerListenersBuilder.d.ts.map +1 -1
  170. package/dist/types/triggers/TriggerListenersFactory.d.ts.map +1 -1
  171. package/dist/types/triggers/TriggerListenersStore.d.ts.map +1 -1
  172. package/dist/types/triggers/TriggerPayloadBuilder.d.ts.map +1 -1
  173. package/dist/types/triggers/TriggerPayloadManager.d.ts.map +1 -1
  174. package/dist/types/triggers/TriggerPayloadPersister.d.ts +3 -1
  175. package/dist/types/triggers/TriggerPayloadPersister.d.ts.map +1 -1
  176. package/dist/types/tsconfig.tsbuildinfo +1 -1
  177. package/dist/types/utils/map.d.ts.map +1 -1
  178. package/dist/types/utils/notifyChannel.d.ts.map +1 -1
  179. package/graphql.codegen.yml +1 -1
  180. package/package.json +18 -16
  181. package/src/ActionsExecutionContainerHookFactory.ts +6 -0
  182. package/src/ActionsMetrics.ts +135 -0
  183. package/src/dispatch/EventDispatcher.ts +36 -11
  184. package/src/dispatch/EventsRepository.ts +68 -26
  185. package/src/dispatch/ProjectDispatcher.ts +27 -11
  186. package/src/dispatch/WebhookTargetHandler.ts +36 -1
  187. package/src/graphql/resolvers/ResolversFactory.ts +1 -1
  188. package/src/graphql/resolvers/mutation/SetVariablesMutationResolver.ts +1 -1
  189. package/src/graphql/resolvers/query/EventsQueryResolver.ts +1 -1
  190. package/src/graphql/resolvers/query/VariablesQueryResolver.ts +1 -4
  191. package/src/graphql/schema/actions.graphql.ts +20 -8
  192. package/src/graphql/schema/index.ts +30 -17
  193. package/src/index.ts +27 -5
  194. package/src/model/VariablesManager.ts +66 -6
  195. package/src/triggers/TriggerPayloadPersister.ts +6 -0
  196. package/tests/cases/unit/actionsMetrics.test.ts +76 -0
  197. package/tests/cases/unit/eventsRepository.test.ts +136 -0
  198. package/tests/cases/unit/variablesManager.test.ts +154 -0
  199. package/tests/cases/unit/webhookHandler.test.ts +80 -24
  200. package/tests/src/event.ts +27 -0
@@ -0,0 +1,154 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import { createConnectionMock, ExpectedQuery } from '@contember/database-tester'
3
+ import { DatabaseContext, DatabaseContextFactory } from '@contember/engine-system-api'
4
+ import { VariablesManager } from '../../../src/model/VariablesManager.js'
5
+ import { testUuid } from '../../src/uuid.js'
6
+
7
+ const selectVariables = (rows: { name: string; value: string }[]): ExpectedQuery => ({
8
+ sql: 'select * from "system"."actions_variable"',
9
+ parameters: [],
10
+ response: { rows },
11
+ })
12
+
13
+ const transaction = (...queries: ExpectedQuery[]): ExpectedQuery[] => [
14
+ { sql: 'BEGIN;', response: { rowCount: 1 } },
15
+ { sql: 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ', response: { rowCount: 1 } },
16
+ ...queries,
17
+ { sql: 'COMMIT;', response: { rowCount: 1 } },
18
+ ]
19
+
20
+ const createDb = (queries: ExpectedQuery[]): DatabaseContext =>
21
+ new DatabaseContextFactory('system', { uuid: () => testUuid(1) }).create(createConnectionMock(queries))
22
+
23
+ describe('variables from the environment', () => {
24
+ test('a project-scoped env variable overrides a stored row', async () => {
25
+ const manager = new VariablesManager({ MY_PROJECT_ACTIONS_VARIABLE_API_KEY: 'from-env' })
26
+ const db = createDb([selectVariables([{ name: 'API_KEY', value: 'from-db' }])])
27
+
28
+ expect(await manager.fetchVariables(db, 'my-project')).toEqual({ API_KEY: 'from-env' })
29
+ })
30
+
31
+ test('a project-scoped env variable works without any stored row', async () => {
32
+ const manager = new VariablesManager({ MY_PROJECT_ACTIONS_VARIABLE_API_KEY: 'from-env' })
33
+ const db = createDb([selectVariables([])])
34
+
35
+ expect(await manager.fetchVariables(db, 'my-project')).toEqual({ API_KEY: 'from-env' })
36
+ })
37
+
38
+ test('the shared DEFAULT_ value applies to every project, the project-scoped one wins', async () => {
39
+ const manager = new VariablesManager({
40
+ DEFAULT_ACTIONS_VARIABLE_API_KEY: 'shared',
41
+ DEFAULT_ACTIONS_VARIABLE_BASE_URL: 'https://example.com',
42
+ MY_PROJECT_ACTIONS_VARIABLE_API_KEY: 'mine',
43
+ })
44
+ const db = createDb([selectVariables([])])
45
+
46
+ expect(await manager.fetchVariables(db, 'my-project')).toEqual({
47
+ API_KEY: 'mine',
48
+ BASE_URL: 'https://example.com',
49
+ })
50
+ })
51
+
52
+ test('another project and unrelated env variables are ignored', async () => {
53
+ const manager = new VariablesManager({
54
+ OTHER_ACTIONS_VARIABLE_API_KEY: 'not-mine',
55
+ MY_PROJECT_ACTIONS_VARIABLE_: 'no-name',
56
+ MY_PROJECT_ACTIONS_VARIABLE_BLANK: '',
57
+ CONTEMBER_ROOT_TOKEN: 'unrelated',
58
+ })
59
+ const db = createDb([selectVariables([{ name: 'API_KEY', value: 'from-db' }])])
60
+
61
+ expect(await manager.fetchVariables(db, 'my-project')).toEqual({ API_KEY: 'from-db' })
62
+ })
63
+
64
+ test('a stored variable is unaffected when the environment supplies nothing', async () => {
65
+ const manager = new VariablesManager({})
66
+ const db = createDb([selectVariables([{ name: 'API_KEY', value: 'from-db' }, { name: 'baseUrl', value: 'http://localhost' }])])
67
+
68
+ expect(await manager.fetchVariables(db, 'my-project')).toEqual({ API_KEY: 'from-db', baseUrl: 'http://localhost' })
69
+ })
70
+
71
+ test('listing reports where each value comes from, sorted by name', async () => {
72
+ const manager = new VariablesManager({ MY_PROJECT_ACTIONS_VARIABLE_API_KEY: 'from-env' })
73
+ const db = createDb([selectVariables([{ name: 'API_KEY', value: 'from-db' }, { name: 'baseUrl', value: 'http://localhost' }])])
74
+
75
+ expect(await manager.listVariables(db, 'my-project')).toEqual([
76
+ { name: 'API_KEY', value: null, source: 'ENVIRONMENT' },
77
+ { name: 'baseUrl', value: 'http://localhost', source: 'DATABASE' },
78
+ ])
79
+ })
80
+
81
+ test('listing never returns an environment value, not even the one it shadows', async () => {
82
+ const manager = new VariablesManager({ MY_PROJECT_ACTIONS_VARIABLE_API_KEY: 'from-env' })
83
+ const db = createDb([selectVariables([{ name: 'API_KEY', value: 'from-db' }])])
84
+
85
+ const listed = JSON.stringify(await manager.listVariables(db, 'my-project'))
86
+ expect(listed).not.toContain('from-env')
87
+ expect(listed).not.toContain('from-db')
88
+ })
89
+
90
+ test('a stored variable named after an Object prototype member is still listed and writable', async () => {
91
+ const manager = new VariablesManager({ MY_PROJECT_ACTIONS_VARIABLE_API_KEY: 'from-env' })
92
+ const db = createDb([selectVariables([{ name: 'toString', value: 'from-db' }])])
93
+
94
+ expect(await manager.listVariables(db, 'my-project')).toEqual([
95
+ { name: 'API_KEY', value: null, source: 'ENVIRONMENT' },
96
+ { name: 'toString', value: 'from-db', source: 'DATABASE' },
97
+ ])
98
+ })
99
+ })
100
+
101
+ describe('setVariables', () => {
102
+ test('rejects a name supplied by the environment without touching the database', async () => {
103
+ const manager = new VariablesManager({ MY_PROJECT_ACTIONS_VARIABLE_API_KEY: 'from-env' })
104
+ const db = createDb([])
105
+
106
+ await expect(manager.setVariables(db, { variables: [{ name: 'API_KEY', value: 'attempted' }] }, 'my-project')).rejects.toThrow(
107
+ 'Variables supplied by the environment are read-only: API_KEY',
108
+ )
109
+ })
110
+
111
+ test('does not mistake an Object prototype member for an environment variable', async () => {
112
+ const manager = new VariablesManager({})
113
+ const db = createDb(transaction(
114
+ selectVariables([]),
115
+ {
116
+ sql:
117
+ 'insert into "system"."actions_variable" ("id", "name", "value") values (?, ?, ?) on conflict ("name") do update set "value" = ?, "updated_at" = NOW()',
118
+ parameters: [testUuid(1), 'toString', 'plain', 'plain'],
119
+ response: { rowCount: 1 },
120
+ },
121
+ ))
122
+
123
+ await manager.setVariables(db, { variables: [{ name: 'toString', value: 'plain' }] }, 'my-project')
124
+ })
125
+
126
+ test('writes a variable the environment does not supply', async () => {
127
+ const manager = new VariablesManager({ MY_PROJECT_ACTIONS_VARIABLE_API_KEY: 'from-env' })
128
+ const db = createDb(transaction(
129
+ selectVariables([]),
130
+ {
131
+ sql:
132
+ 'insert into "system"."actions_variable" ("id", "name", "value") values (?, ?, ?) on conflict ("name") do update set "value" = ?, "updated_at" = NOW()',
133
+ parameters: [testUuid(1), 'baseUrl', 'http://localhost', 'http://localhost'],
134
+ response: { rowCount: 1 },
135
+ },
136
+ ))
137
+
138
+ await manager.setVariables(db, { variables: [{ name: 'baseUrl', value: 'http://localhost' }] }, 'my-project')
139
+ })
140
+
141
+ test('SET mode drops the stored rows that are not submitted', async () => {
142
+ const manager = new VariablesManager({})
143
+ const db = createDb(transaction(
144
+ selectVariables([{ name: 'baseUrl', value: 'http://localhost' }, { name: 'stale', value: 'gone' }]),
145
+ {
146
+ sql: 'delete from "system"."actions_variable" where "name" in (?)',
147
+ parameters: ['stale'],
148
+ response: { rowCount: 1 },
149
+ },
150
+ ))
151
+
152
+ await manager.setVariables(db, { variables: [{ name: 'baseUrl', value: 'http://localhost' }], mode: 'SET' }, 'my-project')
153
+ })
154
+ })
@@ -2,37 +2,16 @@ import { describe, expect, test } from 'bun:test'
2
2
  import { WebhookTargetHandler } from '../../../src/dispatch/WebhookTargetHandler.js'
3
3
  import { FetcherResponse } from '../../../src/dispatch/WebhookFetcher.js'
4
4
  import { createLogger, Logger, TestLoggerHandler } from '@contember/logger'
5
- import { EventRow } from '../../../src/model/types.js'
6
- import { Actions, ActionsPayload } from '@contember/schema'
5
+ import { Actions } from '@contember/schema'
7
6
  import { HandledEvent } from '../../../src/dispatch/types.js'
8
7
  import { testUuid } from '../../src/uuid.js'
8
+ import { createTestEvent, testEventTime } from '../../src/event.js'
9
9
 
10
10
  const assert = {
11
11
  equal: (a: any, b: any) => expect(a).toEqual(b),
12
12
  deepStrictEqual: (a: any, b: any) => expect(a).toStrictEqual(b),
13
13
  }
14
- const now = new Date('2024-06-20T12:00:00Z')
15
- const createTestEvent = (i = 0, row: Partial<EventRow> = {}): EventRow => ({
16
- created_at: now,
17
- id: testUuid(i * 10 + 1),
18
- trigger: 'test',
19
- target: 'test_target',
20
- last_state_change: now,
21
- log: [],
22
- num_retries: 0,
23
- resolved_at: null,
24
- transaction_id: testUuid(i * 10 + 2),
25
- stage_id: testUuid(i * 10 + 3),
26
- visible_at: now,
27
- payload: { foo: 'bar' } as unknown as ActionsPayload.AnyEventPayload,
28
- priority: 1,
29
- schema_id: 1,
30
- state: 'created',
31
- identity_id: testUuid(i * 10 + 4),
32
- ip_address: '127.0.0.1',
33
- user_agent: 'test-agent',
34
- ...row,
35
- })
14
+ const now = testEventTime
36
15
 
37
16
  const dropDuration = (events: HandledEvent[]): HandledEvent[] => {
38
17
  return events.map(it => ({ ...it, result: { ...it.result, durationMs: -1 } }))
@@ -208,6 +187,7 @@ describe('webhook response', () => {
208
187
  },
209
188
  },
210
189
  ])
190
+ assert.deepStrictEqual(testLoggerHandler.messages, [])
211
191
  })
212
192
 
213
193
  test('error response', async () => {
@@ -269,6 +249,17 @@ describe('webhook response', () => {
269
249
  },
270
250
  },
271
251
  ])
252
+
253
+ const warnings = testLoggerHandler.messages.filter(it => it.level.name === 'warn')
254
+ assert.equal(warnings.length, 1)
255
+ assert.equal(warnings[0].message, 'Webhook target responded with an error status')
256
+ // exact match: neither the URL nor the headers may end up in the log
257
+ assert.deepStrictEqual(warnings[0].ownAttributes, {
258
+ target: 'test_target',
259
+ status: 500,
260
+ statusText: 'Err',
261
+ events: [event1.id, event2.id],
262
+ })
272
263
  })
273
264
 
274
265
  test('invalid partial response', async () => {
@@ -330,6 +321,60 @@ describe('webhook response', () => {
330
321
  },
331
322
  },
332
323
  ])
324
+
325
+ const warnings = testLoggerHandler.messages.filter(it => it.level.name === 'warn')
326
+ assert.equal(warnings.length, 1)
327
+ assert.equal(warnings[0].message, 'Webhook target returned a malformed response')
328
+ // exact match: the parser message quotes the payload and must not reach the log
329
+ assert.deepStrictEqual(warnings[0].ownAttributes, {
330
+ target: 'test_target',
331
+ status: 200,
332
+ events: [event1.id, event2.id],
333
+ })
334
+ })
335
+
336
+ test('response naming events outside the batch', async () => {
337
+ const testLoggerHandler = new TestLoggerHandler()
338
+ const logger = createLogger(testLoggerHandler)
339
+
340
+ const webhookHandler = new WebhookTargetHandler({
341
+ fetch(): Promise<FetcherResponse> {
342
+ return Promise.resolve({
343
+ ok: true,
344
+ headers: new Headers([['content-type', 'application/json']]),
345
+ responseText: `{"failures": [{"eventId": "${testUuid(999)}"}]}`,
346
+ status: 200,
347
+ statusText: 'OK',
348
+ })
349
+ },
350
+ })
351
+
352
+ const target: Actions.AnyTarget = {
353
+ name: 'test_target',
354
+ type: 'webhook',
355
+ url: 'http://localhost',
356
+ }
357
+
358
+ const event1 = createTestEvent(0)
359
+ const event2 = createTestEvent(1)
360
+ const result = await webhookHandler.handle({
361
+ logger,
362
+ target: target,
363
+ events: [event1, event2],
364
+ variables: {},
365
+ })
366
+ assert.equal(result.every(it => !it.result.ok), true)
367
+
368
+ const warnings = testLoggerHandler.messages.filter(it => it.level.name === 'warn')
369
+ assert.equal(warnings.length, 1)
370
+ assert.equal(warnings[0].message, 'Webhook target reported failures for event ids outside the batch')
371
+ // exact match: the unknown ids are strings the target chose, only their count may be logged
372
+ assert.deepStrictEqual(warnings[0].ownAttributes, {
373
+ target: 'test_target',
374
+ status: 200,
375
+ unknownEventIds: 1,
376
+ events: [event1.id, event2.id],
377
+ })
333
378
  })
334
379
 
335
380
  test('ignored invalid response', async () => {
@@ -387,6 +432,7 @@ describe('webhook response', () => {
387
432
  },
388
433
  },
389
434
  ])
435
+ assert.deepStrictEqual(testLoggerHandler.messages, [])
390
436
  })
391
437
 
392
438
  test('partially successful response', async () => {
@@ -445,5 +491,15 @@ describe('webhook response', () => {
445
491
  },
446
492
  },
447
493
  ])
494
+
495
+ const warnings = testLoggerHandler.messages.filter(it => it.level.name === 'warn')
496
+ assert.equal(warnings.length, 1)
497
+ assert.equal(warnings[0].message, 'Webhook target reported failed events')
498
+ // exact match: only the events the target rejected, never the reasons it gave
499
+ assert.deepStrictEqual(warnings[0].ownAttributes, {
500
+ target: 'test_target',
501
+ status: 200,
502
+ events: [event1.id],
503
+ })
448
504
  })
449
505
  })
@@ -0,0 +1,27 @@
1
+ import { ActionsPayload } from '@contember/schema'
2
+ import { EventRow } from '../../src/model/types.js'
3
+ import { testUuid } from './uuid.js'
4
+
5
+ export const testEventTime = new Date('2024-06-20T12:00:00Z')
6
+
7
+ export const createTestEvent = (i = 0, row: Partial<EventRow> = {}): EventRow => ({
8
+ created_at: testEventTime,
9
+ id: testUuid(i * 10 + 1),
10
+ trigger: 'test',
11
+ target: 'test_target',
12
+ last_state_change: testEventTime,
13
+ log: [],
14
+ num_retries: 0,
15
+ resolved_at: null,
16
+ transaction_id: testUuid(i * 10 + 2),
17
+ stage_id: testUuid(i * 10 + 3),
18
+ visible_at: testEventTime,
19
+ payload: { foo: 'bar' } as unknown as ActionsPayload.AnyEventPayload,
20
+ priority: 1,
21
+ schema_id: 1,
22
+ state: 'created',
23
+ identity_id: testUuid(i * 10 + 4),
24
+ ip_address: '127.0.0.1',
25
+ user_agent: 'test-agent',
26
+ ...row,
27
+ })