@contember/engine-actions 1.3.0-alpha.13

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 (289) hide show
  1. package/dist/src/ActionsExecutionContainerHookFactory.d.ts +8 -0
  2. package/dist/src/ActionsExecutionContainerHookFactory.d.ts.map +1 -0
  3. package/dist/src/ActionsExecutionContainerHookFactory.js +28 -0
  4. package/dist/src/ActionsExecutionContainerHookFactory.js.map +1 -0
  5. package/dist/src/ImplementationException.d.ts +3 -0
  6. package/dist/src/ImplementationException.d.ts.map +1 -0
  7. package/dist/src/ImplementationException.js +7 -0
  8. package/dist/src/ImplementationException.js.map +1 -0
  9. package/dist/src/ListenerStoreProvider.d.ts +7 -0
  10. package/dist/src/ListenerStoreProvider.d.ts.map +1 -0
  11. package/dist/src/ListenerStoreProvider.js +21 -0
  12. package/dist/src/ListenerStoreProvider.js.map +1 -0
  13. package/dist/src/dispatch/DispatchWorkerSupervisor.d.ts +17 -0
  14. package/dist/src/dispatch/DispatchWorkerSupervisor.d.ts.map +1 -0
  15. package/dist/src/dispatch/DispatchWorkerSupervisor.js +123 -0
  16. package/dist/src/dispatch/DispatchWorkerSupervisor.js.map +1 -0
  17. package/dist/src/dispatch/EventDispatcher.d.ts +25 -0
  18. package/dist/src/dispatch/EventDispatcher.d.ts.map +1 -0
  19. package/dist/src/dispatch/EventDispatcher.js +46 -0
  20. package/dist/src/dispatch/EventDispatcher.js.map +1 -0
  21. package/dist/src/dispatch/EventsRepository.d.ts +22 -0
  22. package/dist/src/dispatch/EventsRepository.d.ts.map +1 -0
  23. package/dist/src/dispatch/EventsRepository.js +138 -0
  24. package/dist/src/dispatch/EventsRepository.js.map +1 -0
  25. package/dist/src/dispatch/ProjectDispatcher.d.ts +22 -0
  26. package/dist/src/dispatch/ProjectDispatcher.d.ts.map +1 -0
  27. package/dist/src/dispatch/ProjectDispatcher.js +103 -0
  28. package/dist/src/dispatch/ProjectDispatcher.js.map +1 -0
  29. package/dist/src/dispatch/TargetHandlerResolver.d.ts +9 -0
  30. package/dist/src/dispatch/TargetHandlerResolver.d.ts.map +1 -0
  31. package/dist/src/dispatch/TargetHandlerResolver.js +17 -0
  32. package/dist/src/dispatch/TargetHandlerResolver.js.map +1 -0
  33. package/dist/src/dispatch/WebhookTargetHandler.d.ts +7 -0
  34. package/dist/src/dispatch/WebhookTargetHandler.d.ts.map +1 -0
  35. package/dist/src/dispatch/WebhookTargetHandler.js +85 -0
  36. package/dist/src/dispatch/WebhookTargetHandler.js.map +1 -0
  37. package/dist/src/dispatch/types.d.ts +27 -0
  38. package/dist/src/dispatch/types.d.ts.map +1 -0
  39. package/dist/src/dispatch/types.js +3 -0
  40. package/dist/src/dispatch/types.js.map +1 -0
  41. package/dist/src/graphql/http/ActionsApiMiddlewareFactory.d.ts +10 -0
  42. package/dist/src/graphql/http/ActionsApiMiddlewareFactory.d.ts.map +1 -0
  43. package/dist/src/graphql/http/ActionsApiMiddlewareFactory.js +31 -0
  44. package/dist/src/graphql/http/ActionsApiMiddlewareFactory.js.map +1 -0
  45. package/dist/src/graphql/http/ActionsContextResolver.d.ts +11 -0
  46. package/dist/src/graphql/http/ActionsContextResolver.d.ts.map +1 -0
  47. package/dist/src/graphql/http/ActionsContextResolver.js +33 -0
  48. package/dist/src/graphql/http/ActionsContextResolver.js.map +1 -0
  49. package/dist/src/graphql/http/ActionsGraphQLHandlerFactory.d.ts +8 -0
  50. package/dist/src/graphql/http/ActionsGraphQLHandlerFactory.d.ts.map +1 -0
  51. package/dist/src/graphql/http/ActionsGraphQLHandlerFactory.js +21 -0
  52. package/dist/src/graphql/http/ActionsGraphQLHandlerFactory.js.map +1 -0
  53. package/dist/src/graphql/http/ActionsWebsocketControllerFactory.d.ts +11 -0
  54. package/dist/src/graphql/http/ActionsWebsocketControllerFactory.d.ts.map +1 -0
  55. package/dist/src/graphql/http/ActionsWebsocketControllerFactory.js +150 -0
  56. package/dist/src/graphql/http/ActionsWebsocketControllerFactory.js.map +1 -0
  57. package/dist/src/graphql/resolvers/ActionsContext.d.ts +9 -0
  58. package/dist/src/graphql/resolvers/ActionsContext.d.ts.map +1 -0
  59. package/dist/src/graphql/resolvers/ActionsContext.js +3 -0
  60. package/dist/src/graphql/resolvers/ActionsContext.js.map +1 -0
  61. package/dist/src/graphql/resolvers/ResolversFactory.d.ts +13 -0
  62. package/dist/src/graphql/resolvers/ResolversFactory.d.ts.map +1 -0
  63. package/dist/src/graphql/resolvers/ResolversFactory.js +32 -0
  64. package/dist/src/graphql/resolvers/ResolversFactory.js.map +1 -0
  65. package/dist/src/graphql/resolvers/index.d.ts +3 -0
  66. package/dist/src/graphql/resolvers/index.d.ts.map +1 -0
  67. package/dist/src/graphql/resolvers/index.js +19 -0
  68. package/dist/src/graphql/resolvers/index.js.map +1 -0
  69. package/dist/src/graphql/resolvers/mutation/ProcessBatchMutationResolver.d.ts +11 -0
  70. package/dist/src/graphql/resolvers/mutation/ProcessBatchMutationResolver.d.ts.map +1 -0
  71. package/dist/src/graphql/resolvers/mutation/ProcessBatchMutationResolver.js +16 -0
  72. package/dist/src/graphql/resolvers/mutation/ProcessBatchMutationResolver.js.map +1 -0
  73. package/dist/src/graphql/resolvers/mutation/SetVariablesMutationResolver.d.ts +11 -0
  74. package/dist/src/graphql/resolvers/mutation/SetVariablesMutationResolver.d.ts.map +1 -0
  75. package/dist/src/graphql/resolvers/mutation/SetVariablesMutationResolver.js +16 -0
  76. package/dist/src/graphql/resolvers/mutation/SetVariablesMutationResolver.js.map +1 -0
  77. package/dist/src/graphql/resolvers/mutation/index.d.ts +3 -0
  78. package/dist/src/graphql/resolvers/mutation/index.d.ts.map +1 -0
  79. package/dist/src/graphql/resolvers/mutation/index.js +19 -0
  80. package/dist/src/graphql/resolvers/mutation/index.js.map +1 -0
  81. package/dist/src/graphql/resolvers/query/EventsQueryResolver.d.ts +11 -0
  82. package/dist/src/graphql/resolvers/query/EventsQueryResolver.d.ts.map +1 -0
  83. package/dist/src/graphql/resolvers/query/EventsQueryResolver.js +43 -0
  84. package/dist/src/graphql/resolvers/query/EventsQueryResolver.js.map +1 -0
  85. package/dist/src/graphql/resolvers/query/VariablesQueryResolver.d.ts +12 -0
  86. package/dist/src/graphql/resolvers/query/VariablesQueryResolver.d.ts.map +1 -0
  87. package/dist/src/graphql/resolvers/query/VariablesQueryResolver.js +16 -0
  88. package/dist/src/graphql/resolvers/query/VariablesQueryResolver.js.map +1 -0
  89. package/dist/src/graphql/resolvers/query/index.d.ts +2 -0
  90. package/dist/src/graphql/resolvers/query/index.d.ts.map +1 -0
  91. package/dist/src/graphql/resolvers/query/index.js +18 -0
  92. package/dist/src/graphql/resolvers/query/index.js.map +1 -0
  93. package/dist/src/graphql/schema/actions.graphql.d.ts +3 -0
  94. package/dist/src/graphql/schema/actions.graphql.d.ts.map +1 -0
  95. package/dist/src/graphql/schema/actions.graphql.js +93 -0
  96. package/dist/src/graphql/schema/actions.graphql.js.map +1 -0
  97. package/dist/src/graphql/schema/index.d.ts +224 -0
  98. package/dist/src/graphql/schema/index.d.ts.map +1 -0
  99. package/dist/src/graphql/schema/index.js +3 -0
  100. package/dist/src/graphql/schema/index.js.map +1 -0
  101. package/dist/src/index.d.ts +13 -0
  102. package/dist/src/index.d.ts.map +1 -0
  103. package/dist/src/index.js +81 -0
  104. package/dist/src/index.js.map +1 -0
  105. package/dist/src/migrations/2023-03-10-145000-actions-variables.d.ts +3 -0
  106. package/dist/src/migrations/2023-03-10-145000-actions-variables.d.ts.map +1 -0
  107. package/dist/src/migrations/2023-03-10-145000-actions-variables.js +17 -0
  108. package/dist/src/migrations/2023-03-10-145000-actions-variables.js.map +1 -0
  109. package/dist/src/migrations/index.d.ts +3 -0
  110. package/dist/src/migrations/index.d.ts.map +1 -0
  111. package/dist/src/migrations/index.js +13 -0
  112. package/dist/src/migrations/index.js.map +1 -0
  113. package/dist/src/migrations/snapshot.d.ts +3 -0
  114. package/dist/src/migrations/snapshot.d.ts.map +1 -0
  115. package/dist/src/migrations/snapshot.js +49 -0
  116. package/dist/src/migrations/snapshot.js.map +1 -0
  117. package/dist/src/model/EventsInProcessingQuery.d.ts +9 -0
  118. package/dist/src/model/EventsInProcessingQuery.d.ts.map +1 -0
  119. package/dist/src/model/EventsInProcessingQuery.js +22 -0
  120. package/dist/src/model/EventsInProcessingQuery.js.map +1 -0
  121. package/dist/src/model/EventsToProcessQuery.d.ts +9 -0
  122. package/dist/src/model/EventsToProcessQuery.d.ts.map +1 -0
  123. package/dist/src/model/EventsToProcessQuery.js +22 -0
  124. package/dist/src/model/EventsToProcessQuery.js.map +1 -0
  125. package/dist/src/model/EventsToProcessSpecification.d.ts +4 -0
  126. package/dist/src/model/EventsToProcessSpecification.d.ts.map +1 -0
  127. package/dist/src/model/EventsToProcessSpecification.js +14 -0
  128. package/dist/src/model/EventsToProcessSpecification.js.map +1 -0
  129. package/dist/src/model/FailedEventsQuery.d.ts +9 -0
  130. package/dist/src/model/FailedEventsQuery.d.ts.map +1 -0
  131. package/dist/src/model/FailedEventsQuery.js +22 -0
  132. package/dist/src/model/FailedEventsQuery.js.map +1 -0
  133. package/dist/src/model/SetVariableCommand.d.ts +8 -0
  134. package/dist/src/model/SetVariableCommand.d.ts.map +1 -0
  135. package/dist/src/model/SetVariableCommand.js +26 -0
  136. package/dist/src/model/SetVariableCommand.js.map +1 -0
  137. package/dist/src/model/VariablesManager.d.ts +8 -0
  138. package/dist/src/model/VariablesManager.d.ts.map +1 -0
  139. package/dist/src/model/VariablesManager.js +46 -0
  140. package/dist/src/model/VariablesManager.js.map +1 -0
  141. package/dist/src/model/VariablesQuery.d.ts +12 -0
  142. package/dist/src/model/VariablesQuery.d.ts.map +1 -0
  143. package/dist/src/model/VariablesQuery.js +13 -0
  144. package/dist/src/model/VariablesQuery.js.map +1 -0
  145. package/dist/src/model/types.d.ts +16 -0
  146. package/dist/src/model/types.d.ts.map +1 -0
  147. package/dist/src/model/types.js +3 -0
  148. package/dist/src/model/types.js.map +1 -0
  149. package/dist/src/triggers/Payload.d.ts +62 -0
  150. package/dist/src/triggers/Payload.d.ts.map +1 -0
  151. package/dist/src/triggers/Payload.js +3 -0
  152. package/dist/src/triggers/Payload.js.map +1 -0
  153. package/dist/src/triggers/TriggerHandler.d.ts +26 -0
  154. package/dist/src/triggers/TriggerHandler.d.ts.map +1 -0
  155. package/dist/src/triggers/TriggerHandler.js +130 -0
  156. package/dist/src/triggers/TriggerHandler.js.map +1 -0
  157. package/dist/src/triggers/TriggerIndirectChangesFetcher.d.ts +11 -0
  158. package/dist/src/triggers/TriggerIndirectChangesFetcher.d.ts.map +1 -0
  159. package/dist/src/triggers/TriggerIndirectChangesFetcher.js +24 -0
  160. package/dist/src/triggers/TriggerIndirectChangesFetcher.js.map +1 -0
  161. package/dist/src/triggers/TriggerListeners.d.ts +9 -0
  162. package/dist/src/triggers/TriggerListeners.d.ts.map +1 -0
  163. package/dist/src/triggers/TriggerListeners.js +3 -0
  164. package/dist/src/triggers/TriggerListeners.js.map +1 -0
  165. package/dist/src/triggers/TriggerListenersBuilder.d.ts +15 -0
  166. package/dist/src/triggers/TriggerListenersBuilder.d.ts.map +1 -0
  167. package/dist/src/triggers/TriggerListenersBuilder.js +169 -0
  168. package/dist/src/triggers/TriggerListenersBuilder.js.map +1 -0
  169. package/dist/src/triggers/TriggerListenersFactory.d.ts +8 -0
  170. package/dist/src/triggers/TriggerListenersFactory.d.ts.map +1 -0
  171. package/dist/src/triggers/TriggerListenersFactory.js +18 -0
  172. package/dist/src/triggers/TriggerListenersFactory.js.map +1 -0
  173. package/dist/src/triggers/TriggerListenersStore.d.ts +48 -0
  174. package/dist/src/triggers/TriggerListenersStore.d.ts.map +1 -0
  175. package/dist/src/triggers/TriggerListenersStore.js +30 -0
  176. package/dist/src/triggers/TriggerListenersStore.js.map +1 -0
  177. package/dist/src/triggers/TriggerPayloadBuilder.d.ts +14 -0
  178. package/dist/src/triggers/TriggerPayloadBuilder.d.ts.map +1 -0
  179. package/dist/src/triggers/TriggerPayloadBuilder.js +140 -0
  180. package/dist/src/triggers/TriggerPayloadBuilder.js.map +1 -0
  181. package/dist/src/triggers/TriggerPayloadManager.d.ts +27 -0
  182. package/dist/src/triggers/TriggerPayloadManager.d.ts.map +1 -0
  183. package/dist/src/triggers/TriggerPayloadManager.js +26 -0
  184. package/dist/src/triggers/TriggerPayloadManager.js.map +1 -0
  185. package/dist/src/triggers/TriggerPayloadPersister.d.ts +18 -0
  186. package/dist/src/triggers/TriggerPayloadPersister.d.ts.map +1 -0
  187. package/dist/src/triggers/TriggerPayloadPersister.js +44 -0
  188. package/dist/src/triggers/TriggerPayloadPersister.js.map +1 -0
  189. package/dist/src/triggers/index.d.ts +8 -0
  190. package/dist/src/triggers/index.d.ts.map +1 -0
  191. package/dist/src/triggers/index.js +24 -0
  192. package/dist/src/triggers/index.js.map +1 -0
  193. package/dist/src/tsconfig.tsbuildinfo +1 -0
  194. package/dist/src/utils/assertNever.d.ts +2 -0
  195. package/dist/src/utils/assertNever.d.ts.map +1 -0
  196. package/dist/src/utils/assertNever.js +8 -0
  197. package/dist/src/utils/assertNever.js.map +1 -0
  198. package/dist/src/utils/map.d.ts +2 -0
  199. package/dist/src/utils/map.d.ts.map +1 -0
  200. package/dist/src/utils/map.js +14 -0
  201. package/dist/src/utils/map.js.map +1 -0
  202. package/dist/tests/cases/integration/triggers.manyHasManyWatch.test.d.ts +2 -0
  203. package/dist/tests/cases/integration/triggers.manyHasManyWatch.test.d.ts.map +1 -0
  204. package/dist/tests/cases/integration/triggers.manyHasManyWatch.test.js +442 -0
  205. package/dist/tests/cases/integration/triggers.manyHasManyWatch.test.js.map +1 -0
  206. package/dist/tests/cases/integration/triggers.manyHasOneWatch.test.d.ts +2 -0
  207. package/dist/tests/cases/integration/triggers.manyHasOneWatch.test.d.ts.map +1 -0
  208. package/dist/tests/cases/integration/triggers.manyHasOneWatch.test.js +449 -0
  209. package/dist/tests/cases/integration/triggers.manyHasOneWatch.test.js.map +1 -0
  210. package/dist/tests/cases/integration/triggers.rootWatch.test.d.ts +2 -0
  211. package/dist/tests/cases/integration/triggers.rootWatch.test.d.ts.map +1 -0
  212. package/dist/tests/cases/integration/triggers.rootWatch.test.js +159 -0
  213. package/dist/tests/cases/integration/triggers.rootWatch.test.js.map +1 -0
  214. package/dist/tests/cases/integration/utils.d.ts +4 -0
  215. package/dist/tests/cases/integration/utils.d.ts.map +1 -0
  216. package/dist/tests/cases/integration/utils.js +12 -0
  217. package/dist/tests/cases/integration/utils.js.map +1 -0
  218. package/dist/tests/cases/unit/generator.test.d.ts +2 -0
  219. package/dist/tests/cases/unit/generator.test.d.ts.map +1 -0
  220. package/dist/tests/cases/unit/generator.test.js +627 -0
  221. package/dist/tests/cases/unit/generator.test.js.map +1 -0
  222. package/dist/tests/src/gql.d.ts +2 -0
  223. package/dist/tests/src/gql.d.ts.map +1 -0
  224. package/dist/tests/src/gql.js +14 -0
  225. package/dist/tests/src/gql.js.map +1 -0
  226. package/dist/tests/tsconfig.tsbuildinfo +1 -0
  227. package/graphql-actions.codegen.yml +21 -0
  228. package/package.json +45 -0
  229. package/src/ActionsExecutionContainerHookFactory.ts +28 -0
  230. package/src/ImplementationException.ts +2 -0
  231. package/src/ListenerStoreProvider.ts +17 -0
  232. package/src/dispatch/DispatchWorkerSupervisor.ts +137 -0
  233. package/src/dispatch/EventDispatcher.ts +64 -0
  234. package/src/dispatch/EventsRepository.ts +165 -0
  235. package/src/dispatch/ProjectDispatcher.ts +119 -0
  236. package/src/dispatch/TargetHandlerResolver.ts +18 -0
  237. package/src/dispatch/WebhookTargetHandler.ts +88 -0
  238. package/src/dispatch/types.ts +31 -0
  239. package/src/graphql/http/ActionsApiMiddlewareFactory.ts +36 -0
  240. package/src/graphql/http/ActionsContextResolver.ts +38 -0
  241. package/src/graphql/http/ActionsGraphQLHandlerFactory.ts +23 -0
  242. package/src/graphql/http/ActionsWebsocketControllerFactory.ts +143 -0
  243. package/src/graphql/resolvers/ActionsContext.ts +9 -0
  244. package/src/graphql/resolvers/ResolversFactory.ts +35 -0
  245. package/src/graphql/resolvers/index.ts +2 -0
  246. package/src/graphql/resolvers/mutation/ProcessBatchMutationResolver.ts +16 -0
  247. package/src/graphql/resolvers/mutation/SetVariablesMutationResolver.ts +19 -0
  248. package/src/graphql/resolvers/mutation/index.ts +2 -0
  249. package/src/graphql/resolvers/query/EventsQueryResolver.ts +55 -0
  250. package/src/graphql/resolvers/query/VariablesQueryResolver.ts +18 -0
  251. package/src/graphql/resolvers/query/index.ts +1 -0
  252. package/src/graphql/schema/actions.graphql.ts +91 -0
  253. package/src/graphql/schema/index.ts +295 -0
  254. package/src/index.ts +88 -0
  255. package/src/migrations/2023-03-10-145000-actions-variables.ts +16 -0
  256. package/src/migrations/index.ts +7 -0
  257. package/src/migrations/snapshot.ts +47 -0
  258. package/src/model/EventsInProcessingQuery.ts +21 -0
  259. package/src/model/EventsToProcessQuery.ts +21 -0
  260. package/src/model/EventsToProcessSpecification.ts +11 -0
  261. package/src/model/FailedEventsQuery.ts +21 -0
  262. package/src/model/SetVariableCommand.ts +26 -0
  263. package/src/model/VariablesManager.ts +49 -0
  264. package/src/model/VariablesQuery.ts +17 -0
  265. package/src/model/types.ts +15 -0
  266. package/src/triggers/Payload.ts +82 -0
  267. package/src/triggers/TriggerHandler.ts +161 -0
  268. package/src/triggers/TriggerIndirectChangesFetcher.ts +30 -0
  269. package/src/triggers/TriggerListeners.ts +16 -0
  270. package/src/triggers/TriggerListenersBuilder.ts +190 -0
  271. package/src/triggers/TriggerListenersFactory.ts +19 -0
  272. package/src/triggers/TriggerListenersStore.ts +76 -0
  273. package/src/triggers/TriggerPayloadBuilder.ts +148 -0
  274. package/src/triggers/TriggerPayloadManager.ts +47 -0
  275. package/src/triggers/TriggerPayloadPersister.ts +43 -0
  276. package/src/triggers/index.ts +7 -0
  277. package/src/tsconfig.json +21 -0
  278. package/src/utils/assertNever.ts +3 -0
  279. package/src/utils/map.ts +13 -0
  280. package/tests/cases/integration/triggers.manyHasManyWatch.test.ts +437 -0
  281. package/tests/cases/integration/triggers.manyHasOneWatch.test.ts +446 -0
  282. package/tests/cases/integration/triggers.rootWatch.test.ts +152 -0
  283. package/tests/cases/integration/utils.ts +9 -0
  284. package/tests/cases/unit/generator.test.ts +689 -0
  285. package/tests/src/gql.ts +7 -0
  286. package/tests/tsconfig.json +16 -0
  287. package/tsconfig.json +5 -0
  288. package/tsconfig.settings.json +8 -0
  289. package/vite.config.js +3 -0
@@ -0,0 +1,88 @@
1
+ import { Actions } from '@contember/schema'
2
+ import { HandledEvent, InvokeHandler, InvokeHandlerArgs } from './types'
3
+ import { VariablesMap } from '../model/VariablesManager'
4
+
5
+ const DEFAULT_TIMEOUT_MS = 30_000 // 30 seconds
6
+
7
+ export class WebhookTargetHandler implements InvokeHandler<Actions.WebhookTarget> {
8
+ public async handle({ target, events, logger, variables }: InvokeHandlerArgs<Actions.WebhookTarget>): Promise<HandledEvent[]> {
9
+ const abortController = new AbortController()
10
+ const timeoutMs = target.timeoutMs
11
+
12
+ const start = process.hrtime.bigint()
13
+ const getDuration = () => Math.floor(Number((process.hrtime.bigint() - start) / BigInt(1_000_000)))
14
+ try {
15
+ const resolvedUrl = this.resolveVariables(target.url, variables)
16
+ const resolvedHeaders = Object.fromEntries(Object.entries(target.headers ?? {})
17
+ .map(([key, value]) => [key, this.resolveVariables(value, variables)]))
18
+
19
+ const response = await withTimeout({ abortController, timeoutMs }, async () => {
20
+
21
+ return await fetch(resolvedUrl, {
22
+ method: 'POST',
23
+ headers: {
24
+ ['User-Agent']: 'Contember Actions',
25
+ ...resolvedHeaders,
26
+ ['Content-type']: 'application/json',
27
+ },
28
+ signal: abortController.signal,
29
+ body: JSON.stringify({
30
+ events: events.map(it => it.payload),
31
+ }),
32
+ })
33
+ })
34
+ let responseText
35
+ try {
36
+ responseText = await withTimeout({ abortController, timeoutMs }, async () => {
37
+ return await response.text()
38
+ })
39
+ } catch {
40
+ }
41
+ const result = {
42
+ ok: response.ok,
43
+ code: response.status,
44
+ durationMs: getDuration(),
45
+ response: responseText,
46
+ errorMessage: !response.ok ? response.statusText : undefined,
47
+ }
48
+
49
+ // todo: per-event response format
50
+
51
+ return events.map(it => ({ target, row: it, result }))
52
+ } catch (e) {
53
+ logger.warn(e)
54
+ const errorMessages = []
55
+ let err = e
56
+ while (typeof err === 'object' && err !== null && 'message' in err && typeof (err as any).message === 'string') {
57
+ errorMessages.push((err as any).message)
58
+ err = (err as any).cause
59
+ }
60
+ const result = {
61
+ ok: false,
62
+ errorMessage: errorMessages.length ? errorMessages.join('; ') : undefined,
63
+ durationMs: getDuration(),
64
+ }
65
+ return events.map(it => ({ target, row: it, result }))
66
+ }
67
+ }
68
+
69
+ private resolveVariables(subject: string, variables: VariablesMap) {
70
+ return subject.replace(/\{\{([\w_]+)}}/gm, (_, name) => {
71
+ if (!variables[name]) {
72
+ throw new Error(`Undefined variable ${name}`)
73
+ }
74
+ return variables[name]
75
+ })
76
+ }
77
+ }
78
+
79
+ const withTimeout = async <T>({ abortController, timeoutMs }: { abortController: AbortController; timeoutMs?: number }, cb: () => Promise<T>): Promise<T> => {
80
+ const timeoutId = setTimeout(() => {
81
+ abortController.abort()
82
+ }, timeoutMs ?? DEFAULT_TIMEOUT_MS)
83
+ try {
84
+ return await cb()
85
+ } finally {
86
+ clearTimeout(timeoutId)
87
+ }
88
+ }
@@ -0,0 +1,31 @@
1
+ import { Logger } from '@contember/logger'
2
+ import { Actions } from '@contember/schema'
3
+ import { EventRow } from '../model/types'
4
+ import { VariablesMap } from '../model/VariablesManager'
5
+
6
+ export { EventRow }
7
+
8
+ export interface InvokeHandler<Target extends Actions.AnyTarget> {
9
+ handle(args: InvokeHandlerArgs<Target>): Promise<HandledEvent[]>
10
+ }
11
+
12
+ export interface InvokeHandlerArgs<Target extends Actions.AnyTarget> {
13
+ target: Target
14
+ events: EventRow[]
15
+ logger: Logger
16
+ variables: VariablesMap
17
+ }
18
+
19
+ export type HandledEvent = {
20
+ row: EventRow
21
+ result: InvocationResult
22
+ target?: Actions.AnyTarget
23
+ }
24
+
25
+ export type InvocationResult = {
26
+ ok: boolean
27
+ durationMs?: number
28
+ errorMessage?: string
29
+ code?: number
30
+ response?: string
31
+ }
@@ -0,0 +1,36 @@
1
+ import { HttpController } from '@contember/engine-http'
2
+ import { ActionsGraphQLHandler } from './ActionsGraphQLHandlerFactory'
3
+ import { ActionsContext } from '../resolvers/ActionsContext'
4
+ import { ActionsContextResolver } from './ActionsContextResolver'
5
+
6
+ export class ActionsApiMiddlewareFactory {
7
+ constructor(
8
+ private readonly actionsContextResolver: ActionsContextResolver,
9
+ private readonly actionsGraphQLHandler: ActionsGraphQLHandler,
10
+ ) {
11
+ }
12
+
13
+ create(): HttpController {
14
+ return async ctx => {
15
+ const { timer, logger, koa } = ctx
16
+ const { projectContainer } = await this.actionsContextResolver.resolve(ctx)
17
+
18
+ const systemDatabase = projectContainer.systemDatabaseContextFactory.create()
19
+
20
+ logger.debug('Actions query processing started')
21
+ const graphqlContext: ActionsContext = {
22
+ db: systemDatabase,
23
+ logger,
24
+ contentSchemaResolver: projectContainer.contentSchemaResolver,
25
+ }
26
+ const handler = this.actionsGraphQLHandler
27
+
28
+ await timer('GraphQL', () => handler({
29
+ request: koa.request,
30
+ response: koa.response,
31
+ createContext: () => graphqlContext,
32
+ }))
33
+ logger.debug('Actions query finished')
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,38 @@
1
+ import { ApplicationContext, HttpErrorResponse, ProjectContextResolver } from '@contember/engine-http'
2
+ import { ProjectRole } from '@contember/schema'
3
+
4
+ export class ActionsContextResolver {
5
+ constructor(
6
+ private readonly debug: boolean,
7
+ private readonly projectContextResolver: ProjectContextResolver,
8
+ ) {
9
+ }
10
+
11
+ async resolve(ctx: ApplicationContext) {
12
+ const { timer, projectGroup: { tenantContainer }, logger, authResult } = ctx
13
+ const projectContext = await this.projectContextResolver.resolve(ctx)
14
+ const { project } = projectContext
15
+ if (!authResult) {
16
+ throw new HttpErrorResponse(401, 'Authentication required')
17
+ }
18
+ const memberships = await timer('MembershipFetch', () =>
19
+ tenantContainer.projectMemberManager.getEffectiveProjectMemberships(
20
+ tenantContainer.databaseContext,
21
+ { slug: project.slug },
22
+ {
23
+ id: authResult.identityId,
24
+ roles: authResult.roles,
25
+ },
26
+ ),
27
+ )
28
+ logger.debug('Memberships fetched', { memberships })
29
+
30
+ const isProjectAdmin = memberships.some(it => it.role === ProjectRole.ADMIN)
31
+ if (!isProjectAdmin) {
32
+ throw this.debug
33
+ ? new HttpErrorResponse(404, `You are not allowed to access project ${project.slug}`)
34
+ : new HttpErrorResponse(404, `Project ${project.slug} NOT found`)
35
+ }
36
+ return projectContext
37
+ }
38
+ }
@@ -0,0 +1,23 @@
1
+ import { makeExecutableSchema } from '@graphql-tools/schema'
2
+ import { createGraphQLQueryHandler, GraphQLQueryHandler } from '@contember/engine-http'
3
+ import { ResolversFactory } from '../resolvers/ResolversFactory'
4
+ import { schema as graphqlDocument } from '../schema/actions.graphql'
5
+ import { ActionsContext } from '../resolvers/ActionsContext'
6
+
7
+
8
+ export type ActionsGraphQLHandler = GraphQLQueryHandler<ActionsContext>
9
+
10
+ export class ActionsGraphQLHandlerFactory {
11
+ create(resolversFactory: ResolversFactory): ActionsGraphQLHandler {
12
+ const resolvers = resolversFactory.create()
13
+ const schema = makeExecutableSchema({
14
+ typeDefs: graphqlDocument,
15
+ resolvers,
16
+ })
17
+
18
+ return createGraphQLQueryHandler<ActionsContext>({
19
+ schema,
20
+ listeners: [],
21
+ })
22
+ }
23
+ }
@@ -0,0 +1,143 @@
1
+ import { HttpErrorResponse, WebSocketController } from '@contember/engine-http'
2
+ import { ActionsContextResolver } from './ActionsContextResolver'
3
+ import { DispatchWorkerSupervisorFactory } from '../../dispatch/DispatchWorkerSupervisor'
4
+ import * as Typesafe from '@contember/typesafe'
5
+ import { TenantRole } from '@contember/engine-tenant-api'
6
+ import { Running } from '@contember/engine-common'
7
+
8
+ const IncomingMessage = Typesafe.discriminatedUnion(
9
+ 'type',
10
+ {
11
+ startWorker: Typesafe.object({}),
12
+ stopAllWorkers: Typesafe.object({}),
13
+ },
14
+ )
15
+ type IncomingMessage = ReturnType<typeof IncomingMessage>
16
+ const OutgoingMessage = Typesafe.discriminatedUnion(
17
+ 'type',
18
+ {
19
+ error: Typesafe.object({
20
+ message: Typesafe.string,
21
+ }),
22
+ message: Typesafe.object({
23
+ message: Typesafe.string,
24
+ }),
25
+ ready: Typesafe.object({}),
26
+
27
+ workerStarted: Typesafe.object({
28
+ workerId: Typesafe.string,
29
+ }),
30
+ workedStopped: Typesafe.object({
31
+ workerId: Typesafe.string,
32
+ }),
33
+ workerFailedToStart: Typesafe.object({
34
+ workerId: Typesafe.string,
35
+ }),
36
+ workerCrashed: Typesafe.object({
37
+ workerId: Typesafe.string,
38
+ }),
39
+ },
40
+ )
41
+
42
+ type OutgoingMessage = ReturnType<typeof OutgoingMessage>
43
+
44
+ export class ActionsWebsocketControllerFactory {
45
+ constructor(
46
+ private readonly debug: boolean,
47
+ private readonly actionsContextResolver: ActionsContextResolver,
48
+ private readonly dispatchWorkerSupervisorFactory: DispatchWorkerSupervisorFactory,
49
+ ) {
50
+ }
51
+
52
+ create(): WebSocketController {
53
+ return async ctx => {
54
+ const { ws, logger, authResult, projectGroup } = ctx
55
+ if (!authResult) {
56
+ throw new HttpErrorResponse(401, 'Authentication required')
57
+ }
58
+ if (!authResult.roles.some(it => it === TenantRole.SUPER_ADMIN || it === TenantRole.PROJECT_ADMIN)) {
59
+ throw new HttpErrorResponse(403, 'Not allowed to run actions worker')
60
+ }
61
+
62
+ const send = (message: OutgoingMessage) => {
63
+ if (this.debug) {
64
+ OutgoingMessage(message)
65
+ }
66
+ ws.send(JSON.stringify(message))
67
+ }
68
+
69
+ let workers: { id: string; running: Running }[] = []
70
+ const abortListener = async () => {
71
+ send({ type: 'message', message: 'shutting down' })
72
+ await stopAll()
73
+ send({ type: 'message', message: 'closing connection' })
74
+ ws.close(1012) // Service Restart
75
+ }
76
+ ctx.abortSignal.addEventListener('abort', abortListener)
77
+
78
+ const stopAll = async () => {
79
+ const currentWorkers = workers.map(it => it.running.end().then(() => send({
80
+ type: 'workedStopped',
81
+ workerId: it.id,
82
+ })))
83
+ workers = []
84
+ await Promise.all(currentWorkers)
85
+ }
86
+ send({ type: 'ready' })
87
+
88
+ ws.addEventListener('message', async message => {
89
+ let json
90
+ try {
91
+ json = JSON.parse(message.data.toString())
92
+ } catch {
93
+ send({ type: 'error', message: 'Invalid JSON' })
94
+ return
95
+ }
96
+ let data: IncomingMessage
97
+ try {
98
+ data = IncomingMessage(json)
99
+ } catch (e) {
100
+ if (e instanceof Typesafe.ParseError) {
101
+ send({ type: 'error', message: e.message })
102
+ return
103
+ } else {
104
+ throw e
105
+ }
106
+ }
107
+
108
+ switch (data.type) {
109
+ case 'startWorker':
110
+ const workerId = Math.random().toString().substring(2)
111
+ const dispatchSupervisor = this.dispatchWorkerSupervisorFactory.create(projectGroup)
112
+ try {
113
+ const running = await dispatchSupervisor.run({ logger, onError: () => {
114
+ send({ type: 'workerCrashed', workerId })
115
+ } })
116
+ workers.push({ id: workerId, running })
117
+ send({ type: 'workerStarted', workerId })
118
+ } catch (e) {
119
+ logger.error(e, { message: 'Worker failed to start' })
120
+ send({ type: 'workerFailedToStart', workerId })
121
+ }
122
+ break
123
+ case 'stopAllWorkers':
124
+ send({ type: 'message', message: 'stopping' })
125
+ await stopAll()
126
+ send({ type: 'message', message: 'all stopped' })
127
+ break
128
+ }
129
+ })
130
+
131
+ const pingHandle = setInterval(() => {
132
+ ws.ping()
133
+ }, 5000)
134
+
135
+ ws.addEventListener('close', async () => {
136
+ ctx.abortSignal.removeEventListener('abort', abortListener)
137
+ clearInterval(pingHandle)
138
+ await stopAll()
139
+ })
140
+ }
141
+ }
142
+ }
143
+
@@ -0,0 +1,9 @@
1
+ import { DatabaseContext } from '@contember/engine-system-api'
2
+ import { ContentSchemaResolver } from '@contember/engine-http'
3
+ import { Logger } from '@contember/logger'
4
+
5
+ export interface ActionsContext {
6
+ logger: Logger
7
+ db: DatabaseContext
8
+ contentSchemaResolver: ContentSchemaResolver
9
+ }
@@ -0,0 +1,35 @@
1
+ import { DateTimeType, JSONType, UuidType } from '@contember/graphql-utils'
2
+ import { Resolvers } from '../schema'
3
+ import { EventsQueryResolver } from './query'
4
+ import { ProcessBatchMutationResolver, SetVariablesMutationResolver } from './mutation'
5
+ import { ActionsContext } from './ActionsContext'
6
+ import { VariablesQueryResolver } from './query/VariablesQueryResolver'
7
+
8
+ export class ResolversFactory {
9
+ constructor(
10
+ private readonly eventsQueryResolver: EventsQueryResolver,
11
+ private readonly processBatchMutationResolver: ProcessBatchMutationResolver,
12
+ private readonly variablesQueryResolver: VariablesQueryResolver,
13
+ private readonly setVariablesMutationResolver: SetVariablesMutationResolver,
14
+ ) {
15
+ }
16
+
17
+ create(): Resolvers {
18
+ const resolvers: Resolvers<ActionsContext> & { Mutation: Required<Resolvers<ActionsContext>['Mutation']> } & { Query: Required<Resolvers<ActionsContext>['Query']> } = {
19
+ Json: JSONType,
20
+ DateTime: DateTimeType,
21
+ Uuid: UuidType,
22
+ Mutation: {
23
+ processBatch: this.processBatchMutationResolver.processBatch.bind(this.processBatchMutationResolver),
24
+ setVariables: this.setVariablesMutationResolver.setVariables.bind(this.setVariablesMutationResolver),
25
+ },
26
+ Query: {
27
+ eventsInProcessing: this.eventsQueryResolver.eventsInProcessing.bind(this.eventsQueryResolver),
28
+ eventsToProcess: this.eventsQueryResolver.eventsToProcess.bind(this.eventsQueryResolver),
29
+ failedEvents: this.eventsQueryResolver.failedEvents.bind(this.eventsQueryResolver),
30
+ variables: this.variablesQueryResolver.variables.bind(this.variablesQueryResolver),
31
+ },
32
+ }
33
+ return resolvers
34
+ }
35
+ }
@@ -0,0 +1,2 @@
1
+ export * from './mutation'
2
+ export * from './query'
@@ -0,0 +1,16 @@
1
+ import { MutationResolvers } from '../../schema'
2
+ import { ActionsContext } from '../ActionsContext'
3
+ import { EventDispatcher } from '../../../dispatch/EventDispatcher'
4
+
5
+ export class ProcessBatchMutationResolver implements MutationResolvers<ActionsContext> {
6
+ constructor(
7
+ private eventDispatcher: EventDispatcher,
8
+ ) {
9
+ }
10
+ async processBatch(parent: unknown, args: unknown, ctx: ActionsContext) {
11
+ await this.eventDispatcher.processBatch(ctx)
12
+ return {
13
+ ok: true,
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,19 @@
1
+ import { MutationResolvers, MutationSetVariablesArgs } from '../../schema'
2
+ import { ActionsContext } from '../ActionsContext'
3
+ import { VariablesManager } from '../../../model/VariablesManager'
4
+
5
+ export class SetVariablesMutationResolver implements MutationResolvers<ActionsContext> {
6
+ constructor(
7
+ private readonly variablesManager: VariablesManager,
8
+ ) {
9
+ }
10
+
11
+
12
+ async setVariables(parent: unknown, { args }: MutationSetVariablesArgs, ctx: ActionsContext) {
13
+ await this.variablesManager.setVariables(ctx.db, args)
14
+
15
+ return {
16
+ ok: true,
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,2 @@
1
+ export * from './ProcessBatchMutationResolver'
2
+ export * from './SetVariablesMutationResolver'
@@ -0,0 +1,55 @@
1
+ import {
2
+ Event,
3
+ QueryEventsInProcessingArgs,
4
+ QueryEventsToProcessArgs,
5
+ QueryFailedEventsArgs,
6
+ QueryResolvers,
7
+ } from '../../schema'
8
+ import { ActionsContext } from '../ActionsContext'
9
+ import { EventsToProcessQuery } from '../../../model/EventsToProcessQuery'
10
+ import { EventRow } from '../../../model/types'
11
+ import { DatabaseContext, StagesQuery } from '@contember/engine-system-api'
12
+ import { Client, DatabaseQuery } from '@contember/database'
13
+ import { EventsInProcessingQuery } from '../../../model/EventsInProcessingQuery'
14
+ import { FailedEventsQuery } from '../../../model/FailedEventsQuery'
15
+
16
+ export class EventsQueryResolver implements QueryResolvers<ActionsContext> {
17
+ async eventsInProcessing(parent: unknown, { args }: QueryEventsInProcessingArgs, ctx: ActionsContext) {
18
+ return await this.fetchEvents(ctx.db, new EventsInProcessingQuery(args ?? {}))
19
+ }
20
+
21
+ async eventsToProcess(parent: unknown, { args }: QueryEventsToProcessArgs, ctx: ActionsContext) {
22
+ return await this.fetchEvents(ctx.db, new EventsToProcessQuery(args ?? {}))
23
+ }
24
+
25
+ async failedEvents(parent: unknown, { args }: QueryFailedEventsArgs, ctx: ActionsContext) {
26
+ return await this.fetchEvents(ctx.db, new FailedEventsQuery(args ?? {}))
27
+ }
28
+
29
+ private async fetchEvents(db: DatabaseContext, query: DatabaseQuery<EventRow[]>) {
30
+ const stages = await this.fetchStagesMap(db)
31
+ return this.mapEventRows(await db.queryHandler.fetch(query), stages)
32
+ }
33
+
34
+ private async fetchStagesMap(db: DatabaseContext): Promise<Record<string, string>> {
35
+ return Object.fromEntries((await db.queryHandler.fetch(new StagesQuery())).map(it => [it.id, it.slug]))
36
+ }
37
+
38
+ private mapEventRows(rows: EventRow[], stagesMap: Record<string, string>): Event[] {
39
+
40
+ return rows.map(row => ({
41
+ id: row.id,
42
+ createdAt: row.created_at,
43
+ resolvedAt: row.created_at,
44
+ lastStateChange: row.last_state_change,
45
+ visibleAt: row.visible_at,
46
+ log: row.log,
47
+ numRetries: row.num_retries,
48
+ payload: row.payload,
49
+ stage: stagesMap[row.stage_id],
50
+ state: row.state,
51
+ target: row.target,
52
+ transactionId: row.transaction_id,
53
+ }))
54
+ }
55
+ }
@@ -0,0 +1,18 @@
1
+ import { QueryResolvers } from '../../schema'
2
+ import { ActionsContext } from '../ActionsContext'
3
+ import { VariablesManager } from '../../../model/VariablesManager'
4
+
5
+ export class VariablesQueryResolver implements QueryResolvers<ActionsContext> {
6
+
7
+ constructor(
8
+ private readonly variablesManager: VariablesManager,
9
+ ) {
10
+ }
11
+
12
+ async variables(parent: unknown, args: unknown, ctx: ActionsContext) {
13
+ return Object.entries(await this.variablesManager.fetchVariables(ctx.db)).map(([name, value]) => ({
14
+ name,
15
+ value,
16
+ }))
17
+ }
18
+ }
@@ -0,0 +1 @@
1
+ export * from './EventsQueryResolver'
@@ -0,0 +1,91 @@
1
+ import { gql } from 'graphql-tag'
2
+ import { DocumentNode } from 'graphql'
3
+
4
+ export const schema: DocumentNode = gql`
5
+ scalar DateTime
6
+ scalar Json
7
+ scalar Uuid
8
+
9
+ schema {
10
+ query: Query
11
+ mutation: Mutation
12
+ }
13
+
14
+ type Query {
15
+ failedEvents(args: EventArgs): [Event!]!
16
+ eventsToProcess(args: EventArgs): [Event!]!
17
+ eventsInProcessing(args: EventArgs): [Event!]!
18
+ variables: [Variable!]!
19
+ }
20
+
21
+ type Mutation {
22
+ processBatch: ProcessBatchResponse!
23
+ setVariables(args: SetVariablesArgs!): SetVariablesResponse!
24
+ }
25
+
26
+ input EventArgs {
27
+ offset: Int
28
+ "Max 10000"
29
+ limit: Int
30
+ }
31
+
32
+ type Event {
33
+ id: Uuid!
34
+ transactionId: Uuid!
35
+ createdAt: DateTime!
36
+ lastStateChange: DateTime!
37
+ resolvedAt: DateTime
38
+ visibleAt: DateTime
39
+ numRetries: Int!
40
+ state: EventState!
41
+ stage: String!
42
+ target: String!
43
+ payload: Json!
44
+ log: Json!
45
+ }
46
+
47
+ enum EventState {
48
+ created
49
+ retrying
50
+ processing
51
+ succeed
52
+ failed
53
+ stopped
54
+ }
55
+
56
+ type ProcessBatchResponse {
57
+ ok: Boolean!
58
+ }
59
+
60
+ input SetVariablesArgs {
61
+ variables: [VariableInput!]!
62
+ mode: SetVariablesMode
63
+ }
64
+
65
+ """
66
+ Defines how it handles original variables.
67
+ - MERGE merges with new values (default behaviour)
68
+ - SET replaces all variables
69
+ - APPEND_ONLY_MISSING appends values if not already exist
70
+ """
71
+ enum SetVariablesMode {
72
+ MERGE
73
+ SET
74
+ APPEND_ONLY_MISSING
75
+ }
76
+
77
+ input VariableInput {
78
+ name: String!
79
+ value: String!
80
+ }
81
+
82
+ type SetVariablesResponse {
83
+ ok: Boolean!
84
+ }
85
+
86
+ type Variable {
87
+ name: String!
88
+ value: String!
89
+ }
90
+
91
+ `