@contember/engine-actions 1.3.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.
- package/dist/src/ActionsExecutionContainerHookFactory.d.ts +8 -0
- package/dist/src/ActionsExecutionContainerHookFactory.d.ts.map +1 -0
- package/dist/src/ActionsExecutionContainerHookFactory.js +28 -0
- package/dist/src/ActionsExecutionContainerHookFactory.js.map +1 -0
- package/dist/src/ImplementationException.d.ts +3 -0
- package/dist/src/ImplementationException.d.ts.map +1 -0
- package/dist/src/ImplementationException.js +7 -0
- package/dist/src/ImplementationException.js.map +1 -0
- package/dist/src/ListenerStoreProvider.d.ts +7 -0
- package/dist/src/ListenerStoreProvider.d.ts.map +1 -0
- package/dist/src/ListenerStoreProvider.js +21 -0
- package/dist/src/ListenerStoreProvider.js.map +1 -0
- package/dist/src/dispatch/DispatchWorkerSupervisor.d.ts +17 -0
- package/dist/src/dispatch/DispatchWorkerSupervisor.d.ts.map +1 -0
- package/dist/src/dispatch/DispatchWorkerSupervisor.js +123 -0
- package/dist/src/dispatch/DispatchWorkerSupervisor.js.map +1 -0
- package/dist/src/dispatch/EventDispatcher.d.ts +25 -0
- package/dist/src/dispatch/EventDispatcher.d.ts.map +1 -0
- package/dist/src/dispatch/EventDispatcher.js +46 -0
- package/dist/src/dispatch/EventDispatcher.js.map +1 -0
- package/dist/src/dispatch/EventsRepository.d.ts +22 -0
- package/dist/src/dispatch/EventsRepository.d.ts.map +1 -0
- package/dist/src/dispatch/EventsRepository.js +138 -0
- package/dist/src/dispatch/EventsRepository.js.map +1 -0
- package/dist/src/dispatch/ProjectDispatcher.d.ts +22 -0
- package/dist/src/dispatch/ProjectDispatcher.d.ts.map +1 -0
- package/dist/src/dispatch/ProjectDispatcher.js +103 -0
- package/dist/src/dispatch/ProjectDispatcher.js.map +1 -0
- package/dist/src/dispatch/TargetHandlerResolver.d.ts +9 -0
- package/dist/src/dispatch/TargetHandlerResolver.d.ts.map +1 -0
- package/dist/src/dispatch/TargetHandlerResolver.js +17 -0
- package/dist/src/dispatch/TargetHandlerResolver.js.map +1 -0
- package/dist/src/dispatch/WebhookTargetHandler.d.ts +7 -0
- package/dist/src/dispatch/WebhookTargetHandler.d.ts.map +1 -0
- package/dist/src/dispatch/WebhookTargetHandler.js +85 -0
- package/dist/src/dispatch/WebhookTargetHandler.js.map +1 -0
- package/dist/src/dispatch/types.d.ts +27 -0
- package/dist/src/dispatch/types.d.ts.map +1 -0
- package/dist/src/dispatch/types.js +3 -0
- package/dist/src/dispatch/types.js.map +1 -0
- package/dist/src/graphql/http/ActionsApiMiddlewareFactory.d.ts +10 -0
- package/dist/src/graphql/http/ActionsApiMiddlewareFactory.d.ts.map +1 -0
- package/dist/src/graphql/http/ActionsApiMiddlewareFactory.js +31 -0
- package/dist/src/graphql/http/ActionsApiMiddlewareFactory.js.map +1 -0
- package/dist/src/graphql/http/ActionsContextResolver.d.ts +11 -0
- package/dist/src/graphql/http/ActionsContextResolver.d.ts.map +1 -0
- package/dist/src/graphql/http/ActionsContextResolver.js +33 -0
- package/dist/src/graphql/http/ActionsContextResolver.js.map +1 -0
- package/dist/src/graphql/http/ActionsGraphQLHandlerFactory.d.ts +8 -0
- package/dist/src/graphql/http/ActionsGraphQLHandlerFactory.d.ts.map +1 -0
- package/dist/src/graphql/http/ActionsGraphQLHandlerFactory.js +21 -0
- package/dist/src/graphql/http/ActionsGraphQLHandlerFactory.js.map +1 -0
- package/dist/src/graphql/http/ActionsWebsocketControllerFactory.d.ts +11 -0
- package/dist/src/graphql/http/ActionsWebsocketControllerFactory.d.ts.map +1 -0
- package/dist/src/graphql/http/ActionsWebsocketControllerFactory.js +150 -0
- package/dist/src/graphql/http/ActionsWebsocketControllerFactory.js.map +1 -0
- package/dist/src/graphql/resolvers/ActionsContext.d.ts +9 -0
- package/dist/src/graphql/resolvers/ActionsContext.d.ts.map +1 -0
- package/dist/src/graphql/resolvers/ActionsContext.js +3 -0
- package/dist/src/graphql/resolvers/ActionsContext.js.map +1 -0
- package/dist/src/graphql/resolvers/ResolversFactory.d.ts +13 -0
- package/dist/src/graphql/resolvers/ResolversFactory.d.ts.map +1 -0
- package/dist/src/graphql/resolvers/ResolversFactory.js +32 -0
- package/dist/src/graphql/resolvers/ResolversFactory.js.map +1 -0
- package/dist/src/graphql/resolvers/index.d.ts +3 -0
- package/dist/src/graphql/resolvers/index.d.ts.map +1 -0
- package/dist/src/graphql/resolvers/index.js +19 -0
- package/dist/src/graphql/resolvers/index.js.map +1 -0
- package/dist/src/graphql/resolvers/mutation/ProcessBatchMutationResolver.d.ts +11 -0
- package/dist/src/graphql/resolvers/mutation/ProcessBatchMutationResolver.d.ts.map +1 -0
- package/dist/src/graphql/resolvers/mutation/ProcessBatchMutationResolver.js +16 -0
- package/dist/src/graphql/resolvers/mutation/ProcessBatchMutationResolver.js.map +1 -0
- package/dist/src/graphql/resolvers/mutation/SetVariablesMutationResolver.d.ts +11 -0
- package/dist/src/graphql/resolvers/mutation/SetVariablesMutationResolver.d.ts.map +1 -0
- package/dist/src/graphql/resolvers/mutation/SetVariablesMutationResolver.js +16 -0
- package/dist/src/graphql/resolvers/mutation/SetVariablesMutationResolver.js.map +1 -0
- package/dist/src/graphql/resolvers/mutation/index.d.ts +3 -0
- package/dist/src/graphql/resolvers/mutation/index.d.ts.map +1 -0
- package/dist/src/graphql/resolvers/mutation/index.js +19 -0
- package/dist/src/graphql/resolvers/mutation/index.js.map +1 -0
- package/dist/src/graphql/resolvers/query/EventsQueryResolver.d.ts +11 -0
- package/dist/src/graphql/resolvers/query/EventsQueryResolver.d.ts.map +1 -0
- package/dist/src/graphql/resolvers/query/EventsQueryResolver.js +43 -0
- package/dist/src/graphql/resolvers/query/EventsQueryResolver.js.map +1 -0
- package/dist/src/graphql/resolvers/query/VariablesQueryResolver.d.ts +12 -0
- package/dist/src/graphql/resolvers/query/VariablesQueryResolver.d.ts.map +1 -0
- package/dist/src/graphql/resolvers/query/VariablesQueryResolver.js +16 -0
- package/dist/src/graphql/resolvers/query/VariablesQueryResolver.js.map +1 -0
- package/dist/src/graphql/resolvers/query/index.d.ts +2 -0
- package/dist/src/graphql/resolvers/query/index.d.ts.map +1 -0
- package/dist/src/graphql/resolvers/query/index.js +18 -0
- package/dist/src/graphql/resolvers/query/index.js.map +1 -0
- package/dist/src/graphql/schema/actions.graphql.d.ts +3 -0
- package/dist/src/graphql/schema/actions.graphql.d.ts.map +1 -0
- package/dist/src/graphql/schema/actions.graphql.js +93 -0
- package/dist/src/graphql/schema/actions.graphql.js.map +1 -0
- package/dist/src/graphql/schema/index.d.ts +224 -0
- package/dist/src/graphql/schema/index.d.ts.map +1 -0
- package/dist/src/graphql/schema/index.js +3 -0
- package/dist/src/graphql/schema/index.js.map +1 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +81 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/migrations/2023-03-10-145000-actions-variables.d.ts +3 -0
- package/dist/src/migrations/2023-03-10-145000-actions-variables.d.ts.map +1 -0
- package/dist/src/migrations/2023-03-10-145000-actions-variables.js +17 -0
- package/dist/src/migrations/2023-03-10-145000-actions-variables.js.map +1 -0
- package/dist/src/migrations/index.d.ts +3 -0
- package/dist/src/migrations/index.d.ts.map +1 -0
- package/dist/src/migrations/index.js +13 -0
- package/dist/src/migrations/index.js.map +1 -0
- package/dist/src/migrations/snapshot.d.ts +3 -0
- package/dist/src/migrations/snapshot.d.ts.map +1 -0
- package/dist/src/migrations/snapshot.js +49 -0
- package/dist/src/migrations/snapshot.js.map +1 -0
- package/dist/src/model/EventsInProcessingQuery.d.ts +9 -0
- package/dist/src/model/EventsInProcessingQuery.d.ts.map +1 -0
- package/dist/src/model/EventsInProcessingQuery.js +22 -0
- package/dist/src/model/EventsInProcessingQuery.js.map +1 -0
- package/dist/src/model/EventsToProcessQuery.d.ts +9 -0
- package/dist/src/model/EventsToProcessQuery.d.ts.map +1 -0
- package/dist/src/model/EventsToProcessQuery.js +22 -0
- package/dist/src/model/EventsToProcessQuery.js.map +1 -0
- package/dist/src/model/EventsToProcessSpecification.d.ts +4 -0
- package/dist/src/model/EventsToProcessSpecification.d.ts.map +1 -0
- package/dist/src/model/EventsToProcessSpecification.js +14 -0
- package/dist/src/model/EventsToProcessSpecification.js.map +1 -0
- package/dist/src/model/FailedEventsQuery.d.ts +9 -0
- package/dist/src/model/FailedEventsQuery.d.ts.map +1 -0
- package/dist/src/model/FailedEventsQuery.js +22 -0
- package/dist/src/model/FailedEventsQuery.js.map +1 -0
- package/dist/src/model/SetVariableCommand.d.ts +8 -0
- package/dist/src/model/SetVariableCommand.d.ts.map +1 -0
- package/dist/src/model/SetVariableCommand.js +26 -0
- package/dist/src/model/SetVariableCommand.js.map +1 -0
- package/dist/src/model/VariablesManager.d.ts +8 -0
- package/dist/src/model/VariablesManager.d.ts.map +1 -0
- package/dist/src/model/VariablesManager.js +46 -0
- package/dist/src/model/VariablesManager.js.map +1 -0
- package/dist/src/model/VariablesQuery.d.ts +12 -0
- package/dist/src/model/VariablesQuery.d.ts.map +1 -0
- package/dist/src/model/VariablesQuery.js +13 -0
- package/dist/src/model/VariablesQuery.js.map +1 -0
- package/dist/src/model/types.d.ts +16 -0
- package/dist/src/model/types.d.ts.map +1 -0
- package/dist/src/model/types.js +3 -0
- package/dist/src/model/types.js.map +1 -0
- package/dist/src/triggers/Payload.d.ts +62 -0
- package/dist/src/triggers/Payload.d.ts.map +1 -0
- package/dist/src/triggers/Payload.js +3 -0
- package/dist/src/triggers/Payload.js.map +1 -0
- package/dist/src/triggers/TriggerHandler.d.ts +26 -0
- package/dist/src/triggers/TriggerHandler.d.ts.map +1 -0
- package/dist/src/triggers/TriggerHandler.js +130 -0
- package/dist/src/triggers/TriggerHandler.js.map +1 -0
- package/dist/src/triggers/TriggerIndirectChangesFetcher.d.ts +11 -0
- package/dist/src/triggers/TriggerIndirectChangesFetcher.d.ts.map +1 -0
- package/dist/src/triggers/TriggerIndirectChangesFetcher.js +24 -0
- package/dist/src/triggers/TriggerIndirectChangesFetcher.js.map +1 -0
- package/dist/src/triggers/TriggerListeners.d.ts +9 -0
- package/dist/src/triggers/TriggerListeners.d.ts.map +1 -0
- package/dist/src/triggers/TriggerListeners.js +3 -0
- package/dist/src/triggers/TriggerListeners.js.map +1 -0
- package/dist/src/triggers/TriggerListenersBuilder.d.ts +15 -0
- package/dist/src/triggers/TriggerListenersBuilder.d.ts.map +1 -0
- package/dist/src/triggers/TriggerListenersBuilder.js +169 -0
- package/dist/src/triggers/TriggerListenersBuilder.js.map +1 -0
- package/dist/src/triggers/TriggerListenersFactory.d.ts +8 -0
- package/dist/src/triggers/TriggerListenersFactory.d.ts.map +1 -0
- package/dist/src/triggers/TriggerListenersFactory.js +18 -0
- package/dist/src/triggers/TriggerListenersFactory.js.map +1 -0
- package/dist/src/triggers/TriggerListenersStore.d.ts +48 -0
- package/dist/src/triggers/TriggerListenersStore.d.ts.map +1 -0
- package/dist/src/triggers/TriggerListenersStore.js +30 -0
- package/dist/src/triggers/TriggerListenersStore.js.map +1 -0
- package/dist/src/triggers/TriggerPayloadBuilder.d.ts +14 -0
- package/dist/src/triggers/TriggerPayloadBuilder.d.ts.map +1 -0
- package/dist/src/triggers/TriggerPayloadBuilder.js +140 -0
- package/dist/src/triggers/TriggerPayloadBuilder.js.map +1 -0
- package/dist/src/triggers/TriggerPayloadManager.d.ts +27 -0
- package/dist/src/triggers/TriggerPayloadManager.d.ts.map +1 -0
- package/dist/src/triggers/TriggerPayloadManager.js +26 -0
- package/dist/src/triggers/TriggerPayloadManager.js.map +1 -0
- package/dist/src/triggers/TriggerPayloadPersister.d.ts +18 -0
- package/dist/src/triggers/TriggerPayloadPersister.d.ts.map +1 -0
- package/dist/src/triggers/TriggerPayloadPersister.js +44 -0
- package/dist/src/triggers/TriggerPayloadPersister.js.map +1 -0
- package/dist/src/triggers/index.d.ts +8 -0
- package/dist/src/triggers/index.d.ts.map +1 -0
- package/dist/src/triggers/index.js +24 -0
- package/dist/src/triggers/index.js.map +1 -0
- package/dist/src/tsconfig.tsbuildinfo +1 -0
- package/dist/src/utils/assertNever.d.ts +2 -0
- package/dist/src/utils/assertNever.d.ts.map +1 -0
- package/dist/src/utils/assertNever.js +8 -0
- package/dist/src/utils/assertNever.js.map +1 -0
- package/dist/src/utils/map.d.ts +2 -0
- package/dist/src/utils/map.d.ts.map +1 -0
- package/dist/src/utils/map.js +14 -0
- package/dist/src/utils/map.js.map +1 -0
- package/dist/tests/cases/integration/triggers.manyHasManyWatch.test.d.ts +2 -0
- package/dist/tests/cases/integration/triggers.manyHasManyWatch.test.d.ts.map +1 -0
- package/dist/tests/cases/integration/triggers.manyHasManyWatch.test.js +442 -0
- package/dist/tests/cases/integration/triggers.manyHasManyWatch.test.js.map +1 -0
- package/dist/tests/cases/integration/triggers.manyHasOneWatch.test.d.ts +2 -0
- package/dist/tests/cases/integration/triggers.manyHasOneWatch.test.d.ts.map +1 -0
- package/dist/tests/cases/integration/triggers.manyHasOneWatch.test.js +449 -0
- package/dist/tests/cases/integration/triggers.manyHasOneWatch.test.js.map +1 -0
- package/dist/tests/cases/integration/triggers.rootWatch.test.d.ts +2 -0
- package/dist/tests/cases/integration/triggers.rootWatch.test.d.ts.map +1 -0
- package/dist/tests/cases/integration/triggers.rootWatch.test.js +159 -0
- package/dist/tests/cases/integration/triggers.rootWatch.test.js.map +1 -0
- package/dist/tests/cases/integration/utils.d.ts +4 -0
- package/dist/tests/cases/integration/utils.d.ts.map +1 -0
- package/dist/tests/cases/integration/utils.js +12 -0
- package/dist/tests/cases/integration/utils.js.map +1 -0
- package/dist/tests/cases/unit/generator.test.d.ts +2 -0
- package/dist/tests/cases/unit/generator.test.d.ts.map +1 -0
- package/dist/tests/cases/unit/generator.test.js +627 -0
- package/dist/tests/cases/unit/generator.test.js.map +1 -0
- package/dist/tests/src/gql.d.ts +2 -0
- package/dist/tests/src/gql.d.ts.map +1 -0
- package/dist/tests/src/gql.js +14 -0
- package/dist/tests/src/gql.js.map +1 -0
- package/dist/tests/tsconfig.tsbuildinfo +1 -0
- package/graphql-actions.codegen.yml +21 -0
- package/package.json +41 -0
- package/src/ActionsExecutionContainerHookFactory.ts +28 -0
- package/src/ImplementationException.ts +2 -0
- package/src/ListenerStoreProvider.ts +17 -0
- package/src/dispatch/DispatchWorkerSupervisor.ts +137 -0
- package/src/dispatch/EventDispatcher.ts +64 -0
- package/src/dispatch/EventsRepository.ts +165 -0
- package/src/dispatch/ProjectDispatcher.ts +119 -0
- package/src/dispatch/TargetHandlerResolver.ts +18 -0
- package/src/dispatch/WebhookTargetHandler.ts +88 -0
- package/src/dispatch/types.ts +31 -0
- package/src/graphql/http/ActionsApiMiddlewareFactory.ts +36 -0
- package/src/graphql/http/ActionsContextResolver.ts +38 -0
- package/src/graphql/http/ActionsGraphQLHandlerFactory.ts +23 -0
- package/src/graphql/http/ActionsWebsocketControllerFactory.ts +143 -0
- package/src/graphql/resolvers/ActionsContext.ts +9 -0
- package/src/graphql/resolvers/ResolversFactory.ts +35 -0
- package/src/graphql/resolvers/index.ts +2 -0
- package/src/graphql/resolvers/mutation/ProcessBatchMutationResolver.ts +16 -0
- package/src/graphql/resolvers/mutation/SetVariablesMutationResolver.ts +19 -0
- package/src/graphql/resolvers/mutation/index.ts +2 -0
- package/src/graphql/resolvers/query/EventsQueryResolver.ts +55 -0
- package/src/graphql/resolvers/query/VariablesQueryResolver.ts +18 -0
- package/src/graphql/resolvers/query/index.ts +1 -0
- package/src/graphql/schema/actions.graphql.ts +91 -0
- package/src/graphql/schema/index.ts +295 -0
- package/src/index.ts +88 -0
- package/src/migrations/2023-03-10-145000-actions-variables.ts +16 -0
- package/src/migrations/index.ts +7 -0
- package/src/migrations/snapshot.ts +47 -0
- package/src/model/EventsInProcessingQuery.ts +21 -0
- package/src/model/EventsToProcessQuery.ts +21 -0
- package/src/model/EventsToProcessSpecification.ts +11 -0
- package/src/model/FailedEventsQuery.ts +21 -0
- package/src/model/SetVariableCommand.ts +26 -0
- package/src/model/VariablesManager.ts +49 -0
- package/src/model/VariablesQuery.ts +17 -0
- package/src/model/types.ts +15 -0
- package/src/triggers/Payload.ts +82 -0
- package/src/triggers/TriggerHandler.ts +161 -0
- package/src/triggers/TriggerIndirectChangesFetcher.ts +30 -0
- package/src/triggers/TriggerListeners.ts +16 -0
- package/src/triggers/TriggerListenersBuilder.ts +190 -0
- package/src/triggers/TriggerListenersFactory.ts +19 -0
- package/src/triggers/TriggerListenersStore.ts +76 -0
- package/src/triggers/TriggerPayloadBuilder.ts +148 -0
- package/src/triggers/TriggerPayloadManager.ts +47 -0
- package/src/triggers/TriggerPayloadPersister.ts +43 -0
- package/src/triggers/index.ts +7 -0
- package/src/tsconfig.json +21 -0
- package/src/utils/assertNever.ts +3 -0
- package/src/utils/map.ts +13 -0
- package/tests/cases/integration/triggers.manyHasManyWatch.test.ts +437 -0
- package/tests/cases/integration/triggers.manyHasOneWatch.test.ts +446 -0
- package/tests/cases/integration/triggers.rootWatch.test.ts +152 -0
- package/tests/cases/integration/utils.ts +9 -0
- package/tests/cases/unit/generator.test.ts +689 -0
- package/tests/src/gql.ts +7 -0
- package/tests/tsconfig.json +16 -0
- package/tsconfig.json +5 -0
- package/tsconfig.settings.json +8 -0
- package/vite.config.js +3 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
schema:
|
|
2
|
+
- "./src/graphql/schema/actions.graphql.ts"
|
|
3
|
+
documents: []
|
|
4
|
+
config:
|
|
5
|
+
immutableTypes: "1"
|
|
6
|
+
generates:
|
|
7
|
+
./src/graphql/schema/index.ts:
|
|
8
|
+
config:
|
|
9
|
+
immutableTypes: "1"
|
|
10
|
+
enumsAsTypes: "1"
|
|
11
|
+
scalars:
|
|
12
|
+
DateTime: Date
|
|
13
|
+
Uuid: String
|
|
14
|
+
Json: any
|
|
15
|
+
plugins:
|
|
16
|
+
- "typescript"
|
|
17
|
+
- "typescript-operations"
|
|
18
|
+
- "typescript-resolvers"
|
|
19
|
+
hooks:
|
|
20
|
+
afterAllFileWrite:
|
|
21
|
+
- eslint --fix
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@contember/engine-actions",
|
|
3
|
+
"version": "1.3.0-alpha.4",
|
|
4
|
+
"license": "proprietary",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"typings": "dist/src/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"generate": "gql-gen --config graphql-actions.codegen.yml",
|
|
9
|
+
"test": "vitest"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@contember/database": "^1.3.0-alpha.4",
|
|
13
|
+
"@contember/database-migrations": "^1.3.0-alpha.4",
|
|
14
|
+
"@contember/engine-common": "^1.3.0-alpha.4",
|
|
15
|
+
"@contember/engine-content-api": "^1.3.0-alpha.4",
|
|
16
|
+
"@contember/engine-system-api": "^1.3.0-alpha.4",
|
|
17
|
+
"@contember/engine-tenant-api": "^1.3.0-alpha.4",
|
|
18
|
+
"@contember/engine-http": "^1.3.0-alpha.4",
|
|
19
|
+
"@contember/engine-plugins": "^1.3.0-alpha.4",
|
|
20
|
+
"@contember/graphql-utils": "^1.3.0-alpha.4",
|
|
21
|
+
"@contember/schema": "^1.3.0-alpha.4",
|
|
22
|
+
"@contember/schema-utils": "^1.3.0-alpha.4",
|
|
23
|
+
"@contember/typesafe": "^1.3.0-alpha.4",
|
|
24
|
+
"@contember/logger": "^1.3.0-alpha.4",
|
|
25
|
+
"@graphql-tools/schema": "^8.3.5",
|
|
26
|
+
"graphql-tag": "^2.12.5"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@contember/engine-api-tester": "^1.3.0-alpha.4",
|
|
30
|
+
"@graphql-codegen/cli": "^2.6.2",
|
|
31
|
+
"@graphql-codegen/typescript": "^2.5.1",
|
|
32
|
+
"@graphql-codegen/typescript-operations": "^2.4.2",
|
|
33
|
+
"@graphql-codegen/typescript-resolvers": "^2.6.6",
|
|
34
|
+
"@types/ws": "^8.5.3",
|
|
35
|
+
"graphql": "^16.5.0"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"graphql": ">= 14.6.0",
|
|
39
|
+
"pg": "^8.5.0"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { TriggerHandler, TriggerIndirectChangesFetcher, TriggerPayloadManager } from './triggers'
|
|
2
|
+
import { ListenerStoreProvider } from './ListenerStoreProvider'
|
|
3
|
+
import { TriggerPayloadBuilder } from './triggers/TriggerPayloadBuilder'
|
|
4
|
+
import { TriggerPayloadPersister } from './triggers/TriggerPayloadPersister'
|
|
5
|
+
import { ExecutionContainerHook } from '@contember/engine-content-api'
|
|
6
|
+
|
|
7
|
+
export class ActionsExecutionContainerHookFactory {
|
|
8
|
+
constructor(
|
|
9
|
+
private readonly listenerStoreProvider: ListenerStoreProvider,
|
|
10
|
+
) {
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
create(): ExecutionContainerHook {
|
|
14
|
+
return builder => {
|
|
15
|
+
return builder.setupService('mapperFactory', (mapperFactory, { whereBuilder, schema, pathFactory, systemSchema, providers, stage, project }) => {
|
|
16
|
+
mapperFactory.hooks.push(mapper => {
|
|
17
|
+
const triggerPayloadPersister = new TriggerPayloadPersister(mapper, mapper.db.forSchema(systemSchema), providers, project.slug, stage.id, schema.id)
|
|
18
|
+
const triggerPayloadBuilder = new TriggerPayloadBuilder(mapper)
|
|
19
|
+
const payloadManager = new TriggerPayloadManager(triggerPayloadBuilder, triggerPayloadPersister)
|
|
20
|
+
const changesFetcher = new TriggerIndirectChangesFetcher(mapper, whereBuilder, pathFactory)
|
|
21
|
+
const listenersStore = this.listenerStoreProvider.getListenerStore(schema)
|
|
22
|
+
const triggerHandler = new TriggerHandler(payloadManager, listenersStore, changesFetcher)
|
|
23
|
+
triggerHandler.attach(mapper.eventManager)
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { TriggerListenersFactory, TriggerListenersStore } from './triggers'
|
|
2
|
+
import { Schema } from '@contember/schema'
|
|
3
|
+
|
|
4
|
+
export class ListenerStoreProvider {
|
|
5
|
+
private listenersStoreCache = new WeakMap<Schema, TriggerListenersStore>()
|
|
6
|
+
|
|
7
|
+
public getListenerStore(schema: Schema): TriggerListenersStore {
|
|
8
|
+
const currentStore = this.listenersStoreCache.get(schema)
|
|
9
|
+
if (currentStore) {
|
|
10
|
+
return currentStore
|
|
11
|
+
}
|
|
12
|
+
const listenersFactory = new TriggerListenersFactory(schema)
|
|
13
|
+
const newStore = listenersFactory.create()
|
|
14
|
+
this.listenersStoreCache.set(schema, newStore)
|
|
15
|
+
return newStore
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { ProjectContainer, ProjectGroupContainer } from '@contember/engine-http'
|
|
2
|
+
import { AcquiringListener } from '@contember/database'
|
|
3
|
+
import { Runnable, RunnableArgs, Running, Supervisor } from '@contember/engine-common'
|
|
4
|
+
import { ProjectDispatcherFactory } from './ProjectDispatcher'
|
|
5
|
+
import { Logger } from '@contember/logger'
|
|
6
|
+
import { ImplementationException } from '../ImplementationException'
|
|
7
|
+
|
|
8
|
+
export class DispatchWorkerSupervisorFactory {
|
|
9
|
+
|
|
10
|
+
constructor(
|
|
11
|
+
private projectDispatcherFactory: ProjectDispatcherFactory,
|
|
12
|
+
) {
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public create(projectGroup: ProjectGroupContainer): DispatchWorkerSupervisor {
|
|
16
|
+
return new DispatchWorkerSupervisor(this.projectDispatcherFactory, projectGroup)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class DispatchWorkerSupervisor implements Runnable {
|
|
21
|
+
constructor(
|
|
22
|
+
private projectDispatcherFactory: ProjectDispatcherFactory,
|
|
23
|
+
private projectGroup: ProjectGroupContainer,
|
|
24
|
+
) {
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public async run({ logger, onError }: RunnableArgs) {
|
|
28
|
+
const projectGroup = this.projectGroup
|
|
29
|
+
const tenantContainer = projectGroup.tenantContainer
|
|
30
|
+
const projectContainerResolver = projectGroup.projectContainerResolver
|
|
31
|
+
|
|
32
|
+
const running = new Map<string, { worker: Promise<Running>; container: ProjectContainer }>()
|
|
33
|
+
|
|
34
|
+
const cleanupListener = projectContainerResolver.on('destroy', async ({ container }) => {
|
|
35
|
+
const runningForProject = running.get(container.project.slug)
|
|
36
|
+
if (!runningForProject) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
running.delete(container.project.slug)
|
|
40
|
+
await (await runningForProject.worker).end()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const refreshProjectWorker = async (container: ProjectContainer, isReloading: boolean) => {
|
|
44
|
+
const db = container.systemDatabaseContextFactory.create()
|
|
45
|
+
const current = running.get(container.project.slug)
|
|
46
|
+
if (current && current.container === container) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
const dispatcher = this.projectDispatcherFactory.create({
|
|
50
|
+
db,
|
|
51
|
+
contentSchemaResolver: container.contentSchemaResolver,
|
|
52
|
+
projectSlug: container.project.slug,
|
|
53
|
+
})
|
|
54
|
+
const supervisedDispatcher = new Supervisor(
|
|
55
|
+
dispatcher,
|
|
56
|
+
{ startupMax: isReloading ? Number.MAX_VALUE : 10 },
|
|
57
|
+
)
|
|
58
|
+
const worker = supervisedDispatcher.run({
|
|
59
|
+
logger,
|
|
60
|
+
onError: () => {
|
|
61
|
+
throw new ImplementationException('supervised project dispatcher crashed')
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
running.set(container.project.slug, { worker, container })
|
|
65
|
+
try {
|
|
66
|
+
if (current) {
|
|
67
|
+
await (await current.worker).end()
|
|
68
|
+
}
|
|
69
|
+
await worker
|
|
70
|
+
logger.debug('Project dispatch worker started', { project: container.project.slug })
|
|
71
|
+
} catch (e) {
|
|
72
|
+
logger.error(e, { message: 'Project dispatch worker start failed', project: container.project.slug })
|
|
73
|
+
running.delete(container.project.slug)
|
|
74
|
+
throw e
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const projectTracker = new Supervisor(new AcquiringListener(
|
|
78
|
+
tenantContainer.databaseContext.client,
|
|
79
|
+
'project_updated',
|
|
80
|
+
async () => {
|
|
81
|
+
logger.debug('Project config change detected, reloading dispatch workers')
|
|
82
|
+
await endProjectWorkers()
|
|
83
|
+
try {
|
|
84
|
+
await startWorkers(true)
|
|
85
|
+
} catch (e) {
|
|
86
|
+
logger.error(e, { message: 'Dispatch workers reload failed, terminating' })
|
|
87
|
+
await endAll()
|
|
88
|
+
onError(e)
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
))
|
|
92
|
+
|
|
93
|
+
const runningProjectTracker = await projectTracker.run({
|
|
94
|
+
logger,
|
|
95
|
+
onError: () => {
|
|
96
|
+
throw new ImplementationException('supervised project tracker crashed')
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
const fetchProjects = async () => await tenantContainer.projectManager.getProjects(tenantContainer.databaseContext)
|
|
100
|
+
|
|
101
|
+
const startWorkers = async (isReloading: boolean) => {
|
|
102
|
+
try {
|
|
103
|
+
const projects = await fetchProjects()
|
|
104
|
+
for (const project of projects) {
|
|
105
|
+
const container = await projectGroup.projectContainerResolver.getProjectContainer(project.slug)
|
|
106
|
+
if (container) {
|
|
107
|
+
await refreshProjectWorker(container, isReloading)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch (e) {
|
|
111
|
+
await endAll()
|
|
112
|
+
throw e
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const endProjectWorkers = async () => {
|
|
116
|
+
const workers = [...running.values()]
|
|
117
|
+
running.clear()
|
|
118
|
+
await Promise.allSettled(workers.map(async ({ worker }) => await (await worker).end()))
|
|
119
|
+
}
|
|
120
|
+
const endAll = async () => {
|
|
121
|
+
logger.debug('Terminating project dispatch workers...')
|
|
122
|
+
cleanupListener()
|
|
123
|
+
try {
|
|
124
|
+
await runningProjectTracker.end()
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
await endProjectWorkers()
|
|
128
|
+
logger.debug('Project dispatch workers terminated')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
await startWorkers(false)
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
end: endAll,
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { EventsRepository } from './EventsRepository'
|
|
2
|
+
import { TargetHandlerResolver } from './TargetHandlerResolver'
|
|
3
|
+
import { HandledEvent } from './types'
|
|
4
|
+
import { Logger } from '@contember/logger'
|
|
5
|
+
import { ContentSchemaResolver } from '@contember/engine-http'
|
|
6
|
+
import { DatabaseContext } from '@contember/engine-system-api'
|
|
7
|
+
import { VariablesManager } from '../model/VariablesManager'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
type ProcessBatchArgs = {
|
|
11
|
+
db: DatabaseContext
|
|
12
|
+
contentSchemaResolver: ContentSchemaResolver
|
|
13
|
+
logger: Logger
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type ProcessBatchResult = {
|
|
17
|
+
succeed: number
|
|
18
|
+
failed: number
|
|
19
|
+
backoffMs: number | undefined
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class EventDispatcher {
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
private readonly eventsRepository: EventsRepository,
|
|
26
|
+
private readonly variablesManager: VariablesManager,
|
|
27
|
+
private readonly invokeResolver: TargetHandlerResolver,
|
|
28
|
+
) {
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async processBatch({ db, contentSchemaResolver, logger }: ProcessBatchArgs): Promise<ProcessBatchResult> {
|
|
32
|
+
const schema = await contentSchemaResolver.getSchema(db)
|
|
33
|
+
const batch = await this.eventsRepository.fetchBatch(schema.actions, db.client)
|
|
34
|
+
const batchId = Math.random().toString().substring(2)
|
|
35
|
+
const batchLogger = logger.child({ loc: 'Actions.EventDispatcher', batchId })
|
|
36
|
+
if (!batch.ok) {
|
|
37
|
+
batchLogger.debug('Nothing to process', {
|
|
38
|
+
backoffMs: batch.backoffMs ?? 'undefined',
|
|
39
|
+
})
|
|
40
|
+
return { failed: 0, succeed: 0, backoffMs: batch.backoffMs }
|
|
41
|
+
}
|
|
42
|
+
const { target, events } = batch
|
|
43
|
+
try {
|
|
44
|
+
const handler = this.invokeResolver.resolveHandler(target)
|
|
45
|
+
batchLogger.debug('Processing started', {
|
|
46
|
+
events: batch.events.map(it => it.id),
|
|
47
|
+
})
|
|
48
|
+
const variables = await this.variablesManager.fetchVariables(db)
|
|
49
|
+
const handledEvents = await handler.handle({ target, events, logger: batchLogger, variables })
|
|
50
|
+
const [succeed, failed] = await this.eventsRepository.persistProcessed(db.client, handledEvents)
|
|
51
|
+
batchLogger.debug('Processing done', { succeed, failed })
|
|
52
|
+
return { succeed, failed, backoffMs: 0 }
|
|
53
|
+
} catch (e) {
|
|
54
|
+
logger.error(e, { loc: 'EventDispatcher', batchId })
|
|
55
|
+
const failedEvents = events.map((it): HandledEvent => ({
|
|
56
|
+
row: it,
|
|
57
|
+
result: { ok: false, errorMessage: `Internal error` },
|
|
58
|
+
}))
|
|
59
|
+
await this.eventsRepository.persistProcessed(db.client, failedEvents)
|
|
60
|
+
|
|
61
|
+
return { succeed: 0, failed: batch.events.length, backoffMs: 0 }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Client,
|
|
3
|
+
Connection,
|
|
4
|
+
Literal,
|
|
5
|
+
LockModifier,
|
|
6
|
+
LockType,
|
|
7
|
+
Operator,
|
|
8
|
+
SelectBuilder,
|
|
9
|
+
UpdateBuilder,
|
|
10
|
+
} from '@contember/database'
|
|
11
|
+
import { EventRow, HandledEvent } from './types'
|
|
12
|
+
import { Actions } from '@contember/schema'
|
|
13
|
+
import { eventsToProcessSpecification, eventsToProcessStateSpecification } from '../model/EventsToProcessSpecification'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
const ACK_TIMEOUT_MS = 1_000 * 60 * 10 // 10 minutes
|
|
17
|
+
const DEFAULT_REPEAT_INTERVAL_MS = 5_000 // 5 seconds
|
|
18
|
+
const DEFAULT_MAX_ATTEMPTS = 10
|
|
19
|
+
const DEFAULT_BATCH_SIZE = 20
|
|
20
|
+
|
|
21
|
+
export class EventsRepository {
|
|
22
|
+
|
|
23
|
+
public async fetchBatch(actions: Actions.Schema, db: Client): Promise<FetchBatchResult> {
|
|
24
|
+
const primaryEvent = (await this.fetchInternal(db, 1))[0]
|
|
25
|
+
if (!primaryEvent) {
|
|
26
|
+
return { ok: false, backoffMs: await this.fetchBackOff(db) }
|
|
27
|
+
}
|
|
28
|
+
const target = actions.targets[primaryEvent.target]
|
|
29
|
+
if (!target) {
|
|
30
|
+
await this.markFailedOnUnknownTarget(db, primaryEvent.target, primaryEvent.id)
|
|
31
|
+
return await this.fetchBatch(actions, db)
|
|
32
|
+
}
|
|
33
|
+
const batchSize = (target.batchSize ?? DEFAULT_BATCH_SIZE) - 1
|
|
34
|
+
const batch = batchSize > 0 ? await this.fetchInternal(db, batchSize) : []
|
|
35
|
+
return { ok: true, events: [primaryEvent, ...batch], target }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
private async fetchBackOff(db: Client): Promise<number | undefined> {
|
|
40
|
+
const result = await SelectBuilder.create<{ ms: number }>()
|
|
41
|
+
.from('actions_event')
|
|
42
|
+
.match(eventsToProcessStateSpecification)
|
|
43
|
+
.select(it => it.raw('ceil(extract(epoch from visible_at - now()) * 1000)'), 'ms')
|
|
44
|
+
.orderBy('visible_at', 'asc')
|
|
45
|
+
.limit(1)
|
|
46
|
+
.getResult(db)
|
|
47
|
+
|
|
48
|
+
return result.length ? Math.max(0, result[0].ms) : undefined
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
private async fetchInternal(db: Client, limit: number, targetName?: string): Promise<EventRow[]> {
|
|
53
|
+
const visibleAt = new Date()
|
|
54
|
+
visibleAt.setTime(visibleAt.getTime() + ACK_TIMEOUT_MS)
|
|
55
|
+
return await UpdateBuilder.create()
|
|
56
|
+
.table('actions_event')
|
|
57
|
+
.with('events', qb => qb
|
|
58
|
+
.select(it => it.raw('*'))
|
|
59
|
+
.from('actions_event')
|
|
60
|
+
.match(eventsToProcessSpecification)
|
|
61
|
+
.where(targetName ? { target: targetName } : {})
|
|
62
|
+
.limit(limit)
|
|
63
|
+
.lock(LockType.forNoKeyUpdate, LockModifier.skipLocked),
|
|
64
|
+
)
|
|
65
|
+
.values<EventRow>({
|
|
66
|
+
state: 'processing',
|
|
67
|
+
last_state_change: new Date(),
|
|
68
|
+
visible_at: visibleAt,
|
|
69
|
+
})
|
|
70
|
+
.from(it => it.from('events').where(it => it.columnsEq(['events', 'id'], ['actions_event', 'id'])))
|
|
71
|
+
.returning<EventRow>(new Literal('*'))
|
|
72
|
+
.execute(db)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public async persistProcessed(db: Client, events: HandledEvent[]): Promise<[number, number]> {
|
|
76
|
+
return await db.transaction(async trx => {
|
|
77
|
+
await trx.query(Connection.REPEATABLE_READ)
|
|
78
|
+
const succeed = events.filter(it => it.result.ok)
|
|
79
|
+
await this.markSucceed(trx, succeed.map(it => it.row))
|
|
80
|
+
|
|
81
|
+
for (const event of events) {
|
|
82
|
+
if (!event.result.ok) {
|
|
83
|
+
await this.markFailed(trx, event)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return [succeed.length, events.length - succeed.length]
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public async requeue(db: Client, events: EventRow[]): Promise<void> {
|
|
91
|
+
await UpdateBuilder.create()
|
|
92
|
+
.table('actions_event')
|
|
93
|
+
.values<EventRow>({
|
|
94
|
+
state: it => it.raw('CASE WHEN num_retries = 0 THEN ? ELSE ? END', 'created', 'retrying'),
|
|
95
|
+
last_state_change: new Date(),
|
|
96
|
+
visible_at: new Date(),
|
|
97
|
+
})
|
|
98
|
+
.where(it => it.in('id', events.map(it => it.id)))
|
|
99
|
+
.execute(db)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private async markSucceed(db: Client, rows: EventRow[]): Promise<void> {
|
|
103
|
+
const now = new Date()
|
|
104
|
+
await UpdateBuilder.create()
|
|
105
|
+
.table('actions_event')
|
|
106
|
+
.values<EventRow>({
|
|
107
|
+
state: 'succeed',
|
|
108
|
+
num_retries: it => it.raw('num_retries + 1'),
|
|
109
|
+
resolved_at: now,
|
|
110
|
+
last_state_change: now,
|
|
111
|
+
})
|
|
112
|
+
.where(it => it.in('id', rows.map(it => it.id)))
|
|
113
|
+
.execute(db)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private async markFailed(db: Client, event: HandledEvent): Promise<void> {
|
|
117
|
+
const numRetries = event.row.num_retries
|
|
118
|
+
const now = new Date()
|
|
119
|
+
const nextAttempt = new Date()
|
|
120
|
+
nextAttempt.setTime(nextAttempt.getTime() + (event.target?.initialRepeatIntervalMs ?? DEFAULT_REPEAT_INTERVAL_MS) * Math.pow(2, event.row.num_retries))
|
|
121
|
+
await UpdateBuilder.create()
|
|
122
|
+
.table('actions_event')
|
|
123
|
+
.values<EventRow>({
|
|
124
|
+
last_state_change: now,
|
|
125
|
+
num_retries: event.row.num_retries + 1,
|
|
126
|
+
log: JSON.stringify([...event.row.log, event.result]) as any,
|
|
127
|
+
...(numRetries < (event.target?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS) ? {
|
|
128
|
+
state: 'retrying',
|
|
129
|
+
visible_at: nextAttempt,
|
|
130
|
+
} : {
|
|
131
|
+
state: 'failed',
|
|
132
|
+
}),
|
|
133
|
+
})
|
|
134
|
+
.where({ id: event.row.id })
|
|
135
|
+
.execute(db)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private async markFailedOnUnknownTarget(db: Client, targetName: string, primaryEventId: string) {
|
|
139
|
+
await UpdateBuilder.create()
|
|
140
|
+
.table('actions_event')
|
|
141
|
+
.where(it => it.or(it => it
|
|
142
|
+
.and(it => it
|
|
143
|
+
.in('state', ['retrying', 'created', 'processing'])
|
|
144
|
+
.compare('visible_at', Operator.lte, 'now')
|
|
145
|
+
.compare('target', Operator.eq, targetName),
|
|
146
|
+
)
|
|
147
|
+
.and(it => it
|
|
148
|
+
.compare('id', Operator.eq, primaryEventId),
|
|
149
|
+
),
|
|
150
|
+
))
|
|
151
|
+
.values<EventRow>({
|
|
152
|
+
// intentionally not retrying
|
|
153
|
+
last_state_change: new Date(),
|
|
154
|
+
num_retries: it => it.raw('num_retries + 1'),
|
|
155
|
+
log: it => it.raw('log || ?::jsonb', { ok: false, errorMessage: `Event target ${targetName} not found, stopping.` }),
|
|
156
|
+
state: 'failed',
|
|
157
|
+
resolved_at: new Date(),
|
|
158
|
+
})
|
|
159
|
+
.execute(db)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export type FetchBatchResult =
|
|
164
|
+
| { ok: true; events: EventRow[]; target: Actions.AnyTarget }
|
|
165
|
+
| { ok: false; backoffMs?: number }
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { DatabaseContext } from '@contember/engine-system-api'
|
|
2
|
+
import { EventDispatcher } from './EventDispatcher'
|
|
3
|
+
import { ContentSchemaResolver } from '@contember/engine-http'
|
|
4
|
+
import { Runnable, RunnableArgs, Running } from '@contember/engine-common'
|
|
5
|
+
import { Listener } from '@contember/database'
|
|
6
|
+
import { NOTIFY_CHANNEL_NAME } from '../triggers/TriggerPayloadPersister'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export class ProjectDispatcherFactory {
|
|
10
|
+
constructor(
|
|
11
|
+
private readonly dispatcher: EventDispatcher,
|
|
12
|
+
) {
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public create({ db, contentSchemaResolver, projectSlug }: { db: DatabaseContext; contentSchemaResolver: ContentSchemaResolver; projectSlug: string}): ProjectDispatcher {
|
|
16
|
+
return new ProjectDispatcher(this.dispatcher, db, contentSchemaResolver, projectSlug)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
export class ProjectDispatcher implements Runnable {
|
|
23
|
+
constructor(
|
|
24
|
+
private readonly dispatcher: EventDispatcher,
|
|
25
|
+
private readonly db: DatabaseContext,
|
|
26
|
+
private readonly contentSchemaResolver: ContentSchemaResolver,
|
|
27
|
+
private readonly projectSlug: string,
|
|
28
|
+
) {
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public async run({ logger, onError, onClose }: RunnableArgs): Promise<Running> {
|
|
32
|
+
|
|
33
|
+
return await new Promise<Running>(async (resolve, reject) => {
|
|
34
|
+
|
|
35
|
+
let resolvePending = () => {}
|
|
36
|
+
let rejectPending = (e: any) => {}
|
|
37
|
+
|
|
38
|
+
const resolvePendingRef = () => {
|
|
39
|
+
resolvePending()
|
|
40
|
+
}
|
|
41
|
+
let pendingError: any = undefined
|
|
42
|
+
const rejectPendingRef = (e: any) => {
|
|
43
|
+
pendingError = e
|
|
44
|
+
rejectPending(e)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let succeedTotal = 0
|
|
48
|
+
let failedTotal = 0
|
|
49
|
+
let aborted = false
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
await this.db.scope(async db => {
|
|
53
|
+
const listener = await new Listener<string>(db.client, NOTIFY_CHANNEL_NAME, project => {
|
|
54
|
+
if (project === this.projectSlug) {
|
|
55
|
+
resolvePendingRef()
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
.run({ onError: rejectPendingRef, onClose: resolvePendingRef })
|
|
59
|
+
|
|
60
|
+
resolve({
|
|
61
|
+
end: async () => {
|
|
62
|
+
aborted = true
|
|
63
|
+
await listener.end()
|
|
64
|
+
logger.info('Worker terminated', {
|
|
65
|
+
succeed: succeedTotal,
|
|
66
|
+
failed: failedTotal,
|
|
67
|
+
})
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
while (!aborted) {
|
|
73
|
+
const { succeed, failed, backoffMs } = await this.dispatcher.processBatch({
|
|
74
|
+
db,
|
|
75
|
+
contentSchemaResolver: this.contentSchemaResolver,
|
|
76
|
+
logger,
|
|
77
|
+
})
|
|
78
|
+
succeedTotal += succeed
|
|
79
|
+
failedTotal += failed
|
|
80
|
+
|
|
81
|
+
// listener error occurred during batch processing, rethrow
|
|
82
|
+
if (pendingError) {
|
|
83
|
+
throw pendingError
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// queue is empty, wait
|
|
87
|
+
if (backoffMs !== 0) {
|
|
88
|
+
await new Promise<void>((resolve, reject) => {
|
|
89
|
+
let cleanup = () => {
|
|
90
|
+
}
|
|
91
|
+
if (backoffMs !== undefined) {
|
|
92
|
+
const timeoutHandle = setTimeout(resolve, backoffMs)
|
|
93
|
+
cleanup = () => clearTimeout(timeoutHandle)
|
|
94
|
+
}
|
|
95
|
+
resolvePending = () => {
|
|
96
|
+
resolve()
|
|
97
|
+
cleanup()
|
|
98
|
+
}
|
|
99
|
+
rejectPending = e => {
|
|
100
|
+
reject(e)
|
|
101
|
+
cleanup()
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
onClose?.()
|
|
107
|
+
} catch (e) {
|
|
108
|
+
await listener.end()
|
|
109
|
+
onError(e)
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
} catch (e) {
|
|
113
|
+
reject(e)
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Actions } from '@contember/schema'
|
|
2
|
+
import { WebhookTargetHandler } from './WebhookTargetHandler'
|
|
3
|
+
import { ImplementationException } from '../ImplementationException'
|
|
4
|
+
import { InvokeHandler } from './types'
|
|
5
|
+
|
|
6
|
+
export class TargetHandlerResolver {
|
|
7
|
+
constructor(
|
|
8
|
+
private readonly webhookHandler: WebhookTargetHandler,
|
|
9
|
+
) {
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public resolveHandler<T extends Actions.AnyTarget>(target: T): InvokeHandler<T> {
|
|
13
|
+
if (target.type !== 'webhook') {
|
|
14
|
+
throw new ImplementationException('Invalid trigger invocation type')
|
|
15
|
+
}
|
|
16
|
+
return this.webhookHandler
|
|
17
|
+
}
|
|
18
|
+
}
|