@wmfs/tymly-schedule-plugin 1.0.1 → 1.1.0

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 (38) hide show
  1. package/.version +1 -1
  2. package/CHANGELOG.md +26 -0
  3. package/lib/blueprints/schedule-blueprint/card-templates/create-scheduled-task.json +97 -0
  4. package/lib/blueprints/schedule-blueprint/card-templates/view-all-scheduled-tasks.json +36 -0
  5. package/lib/blueprints/schedule-blueprint/card-templates/view-scheduled-task.json +148 -0
  6. package/lib/blueprints/schedule-blueprint/categories/schedule.json +8 -0
  7. package/lib/blueprints/schedule-blueprint/functions/generate-key.js +16 -0
  8. package/lib/blueprints/schedule-blueprint/functions/get-available-state-machines.js +18 -0
  9. package/lib/blueprints/schedule-blueprint/models/task.json +0 -14
  10. package/lib/blueprints/schedule-blueprint/state-machines/create-scheduled-task.json +66 -0
  11. package/lib/blueprints/schedule-blueprint/state-machines/modify-scheduled-task.json +74 -0
  12. package/lib/blueprints/schedule-blueprint/state-machines/start-task.json +36 -0
  13. package/lib/blueprints/schedule-blueprint/state-machines/stop-task.json +36 -0
  14. package/lib/blueprints/schedule-blueprint/state-machines/view-all-scheduled-tasks.json +69 -0
  15. package/lib/blueprints/schedule-blueprint/state-machines/view-scheduled-task.json +46 -0
  16. package/lib/blueprints/schedule-blueprint/template-roles/schedule-admin.json +4 -0
  17. package/lib/components/services/schedule/index.js +77 -23
  18. package/lib/components/state-resources/create-scheduled-task/doc/example.json +8 -0
  19. package/lib/components/state-resources/create-scheduled-task/doc/index.js +4 -0
  20. package/lib/components/state-resources/create-scheduled-task/index.js +56 -0
  21. package/lib/components/state-resources/find-scheduled-executions/doc/example.json +8 -0
  22. package/lib/components/state-resources/find-scheduled-executions/doc/index.js +4 -0
  23. package/lib/components/state-resources/find-scheduled-executions/index.js +12 -0
  24. package/lib/components/state-resources/start-scheduled-task/doc/example.json +8 -0
  25. package/lib/components/state-resources/start-scheduled-task/doc/index.js +4 -0
  26. package/lib/components/state-resources/start-scheduled-task/index.js +12 -0
  27. package/lib/components/state-resources/stop-scheduled-task/doc/example.json +8 -0
  28. package/lib/components/state-resources/stop-scheduled-task/doc/index.js +4 -0
  29. package/lib/components/state-resources/stop-scheduled-task/index.js +12 -0
  30. package/lib/components/state-resources/update-scheduled-state-machine-config/doc/example.json +12 -0
  31. package/lib/components/state-resources/update-scheduled-state-machine-config/doc/index.js +4 -0
  32. package/lib/components/state-resources/update-scheduled-state-machine-config/index.js +35 -0
  33. package/lib/components/state-resources/update-scheduled-task/doc/example.json +13 -0
  34. package/lib/components/state-resources/update-scheduled-task/doc/index.js +4 -0
  35. package/lib/components/state-resources/update-scheduled-task/index.js +26 -0
  36. package/package.json +2 -1
  37. package/plugin.json +1 -1
  38. package/test/schedule-tests.js +17 -14
package/.version CHANGED
@@ -1 +1 @@
1
- VERSION=1.0.1
1
+ VERSION=1.1.0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,29 @@
1
+ # [1.1.0](https://github.com/wmfs/tymly-schedule-plugin/compare/v1.0.1...v1.1.0) (2022-03-16)
2
+
3
+
4
+ ### ✨ Features
5
+
6
+ * ability to create/modify tasks from ui ([b7d56c5](https://github.com/wmfs/tymly-schedule-plugin/commit/b7d56c5e44609d4b2b14dbdc4e13a6c91f784eee))
7
+ * admin tools to create scheduled task ([b0e6ddc](https://github.com/wmfs/tymly-schedule-plugin/commit/b0e6ddc5325ace6332516275893471b39703c1ba))
8
+ * admin tools to start/stop tasks ([106f5fa](https://github.com/wmfs/tymly-schedule-plugin/commit/106f5fa7daedca13030b49fac473a891c5c6d9e1))
9
+ * admin tools to update schedule of state machine ([c6df466](https://github.com/wmfs/tymly-schedule-plugin/commit/c6df4669823e002916d4b86a0458e4e1d57ab45d))
10
+ * admin tools to update state machine config ([e26928a](https://github.com/wmfs/tymly-schedule-plugin/commit/e26928a276c1783fb92785eb2355570dd01f32ac))
11
+ * admin tools to view all and view specific tasks ([b9f80c8](https://github.com/wmfs/tymly-schedule-plugin/commit/b9f80c89077115ae2c783c0b03e8de322111319f))
12
+ * admin tools to view scheduled executions ([89b9094](https://github.com/wmfs/tymly-schedule-plugin/commit/89b90941de1b48a14896043ec3710df2c2440949))
13
+ * remove last ran start/end and total run count, as these can be found from executions ([d6c673b](https://github.com/wmfs/tymly-schedule-plugin/commit/d6c673b926654c564d08f6de1b5f22bcf61b3783))
14
+
15
+
16
+ ### 🐛 Bug Fixes
17
+
18
+ * issue with finding scheduled executions ([b198bd1](https://github.com/wmfs/tymly-schedule-plugin/commit/b198bd1bf49ffb517229309eadaf7fdaed9f1ff7))
19
+ * use function from service to get executions ([b9082bc](https://github.com/wmfs/tymly-schedule-plugin/commit/b9082bc49ccdbd39c1316c786f83efddd7396a7d))
20
+
21
+
22
+ ### 🚨 Tests
23
+
24
+ * fix status after re-boot with datetime schedule in past ([e4e9cba](https://github.com/wmfs/tymly-schedule-plugin/commit/e4e9cba84fbdaf91838e880b7dacda4659e60119))
25
+ * remove unused executions model ([92e888d](https://github.com/wmfs/tymly-schedule-plugin/commit/92e888d266803dac531a27f0c8d74ff6d6b22e52))
26
+
1
27
  ## [1.0.1](https://github.com/wmfs/tymly-schedule-plugin/compare/v1.0.0...v1.0.1) (2022-03-15)
2
28
 
3
29
 
@@ -0,0 +1,97 @@
1
+ {
2
+ "templateMeta": {
3
+ "name": "create-scheduled-task",
4
+ "title": "Configure a Scheduled State Machine",
5
+ "category": "schedule"
6
+ },
7
+ "hooks": {
8
+ "afterSubmit": {
9
+ "actions": [
10
+ {
11
+ "type": "Action.PushCard",
12
+ "stateMachineName": "schedule_viewScheduledTask_1_0",
13
+ "input": {
14
+ "key": "$.key"
15
+ }
16
+ }
17
+ ]
18
+ }
19
+ },
20
+ "type": "AdaptiveCard",
21
+ "body": [
22
+ {
23
+ "showWhen": "data.editing !== true",
24
+ "id": "name",
25
+ "type": "Input.Text",
26
+ "title": "Schedule name",
27
+ "validation": {
28
+ "requiredIf": "data.editing !== true"
29
+ }
30
+ },
31
+ {
32
+ "id": "scheduleType",
33
+ "type": "Input.ChoiceSet",
34
+ "title": "What type of schedule?",
35
+ "choices": [
36
+ {
37
+ "title": "Interval",
38
+ "value": "interval"
39
+ },
40
+ {
41
+ "title": "Date/time",
42
+ "value": "datetime"
43
+ }
44
+ ],
45
+ "validation": {
46
+ "required": true
47
+ }
48
+ },
49
+ {
50
+ "showWhen": "data.scheduleType === 'interval'",
51
+ "id": "interval",
52
+ "type": "Input.Text",
53
+ "title": "Interval",
54
+ "validation": {
55
+ "requiredIf": "data.scheduleType === 'interval'"
56
+ }
57
+ },
58
+ {
59
+ "showWhen": "data.scheduleType === 'datetime'",
60
+ "id": "datetime",
61
+ "type": "Input.DateTime",
62
+ "title": "Date/time",
63
+ "validation": {
64
+ "requiredIf": "data.scheduleType === 'datetime'"
65
+ }
66
+ },
67
+ {
68
+ "id": "stateMachineName",
69
+ "type": "Input.ChoiceSet",
70
+ "title": "Which state machine?",
71
+ "filter": true,
72
+ "choicesPath": "data.availableStateMachines",
73
+ "validation": {
74
+ "required": true
75
+ }
76
+ },
77
+ {
78
+ "id": "inputStr",
79
+ "type": "Input.Text",
80
+ "title": "Input",
81
+ "isMultiline": true,
82
+ "value": "{}"
83
+ }
84
+ ],
85
+ "actions": [
86
+ {
87
+ "type": "Action.Cancel",
88
+ "title": "Cancel"
89
+ },
90
+ {
91
+ "type": "Action.Submit",
92
+ "title": "Submit"
93
+ }
94
+ ],
95
+ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
96
+ "version": "1.0"
97
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "templateMeta": {
3
+ "name": "view-scheduled-tasks",
4
+ "title": "View Scheduled State Machines",
5
+ "category": "schedule"
6
+ },
7
+ "type": "AdaptiveCard",
8
+ "body": [
9
+ {
10
+ "type": "MarkupTable",
11
+ "title": "Scheduled State Machines",
12
+ "arrayPath": "data.tasks",
13
+ "clickToLaunch": true,
14
+ "columns": [
15
+ {
16
+ "title": "Key",
17
+ "value": "{{ item.key }}"
18
+ },
19
+ {
20
+ "title": "Schedule",
21
+ "value": "{{ item.scheduleType === 'datetime' ? item.datetime : item.interval }}"
22
+ },
23
+ {
24
+ "title": "State machine name",
25
+ "value": "{{ item.stateMachineName }}"
26
+ },
27
+ {
28
+ "title": "Status",
29
+ "value": "{{ item.status }}"
30
+ }
31
+ ]
32
+ }
33
+ ],
34
+ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
35
+ "version": "1.0"
36
+ }
@@ -0,0 +1,148 @@
1
+ {
2
+ "templateMeta": {
3
+ "name": "view-scheduled-task",
4
+ "title": "View Scheduled State Machine",
5
+ "category": "schedule"
6
+ },
7
+ "type": "AdaptiveCard",
8
+ "body": [
9
+ {
10
+ "type": "Container",
11
+ "color": "accent",
12
+ "spacing": "none",
13
+ "items": [
14
+ {
15
+ "type": "TextBlock",
16
+ "text": "{{ data.key }}",
17
+ "weight": "bolder",
18
+ "spacing": "none"
19
+ }
20
+ ]
21
+ },
22
+ {
23
+ "type": "ColumnSet",
24
+ "columns": [
25
+ {
26
+ "type": "Column",
27
+ "width": 8,
28
+ "items": [
29
+ {
30
+ "type": "FactSet",
31
+ "showAllIfUndefined": true,
32
+ "facts": [
33
+ {
34
+ "title": "Key",
35
+ "value": "{{ data.key }}"
36
+ },
37
+ {
38
+ "title": "Schedule",
39
+ "value": "{{ data.scheduleType === 'datetime' ? data.datetime : data.interval }}"
40
+ },
41
+ {
42
+ "title": "State machine name",
43
+ "value": "{{ data.stateMachineName }}"
44
+ },
45
+ {
46
+ "title": "Input",
47
+ "value": "{{ JSON.stringify(data.input) }}"
48
+ },
49
+ {
50
+ "title": "Send response",
51
+ "value": "{{ data.sendResponse }}"
52
+ },
53
+ {
54
+ "title": "Start on boot?",
55
+ "value": "{{ data.startOnBoot ? 'Yes' : 'No' }}"
56
+ },
57
+ {
58
+ "title": "Status",
59
+ "value": "{{ data.status }}"
60
+ },
61
+ {
62
+ "title": "Created",
63
+ "value": "{{ formatDate(data.created, 'DD/MM/YYYY HH:mm') }}"
64
+ },
65
+ {
66
+ "title": "Created by",
67
+ "value": "{{ data.createdBy || '-' }}"
68
+ },
69
+ {
70
+ "title": "Modified",
71
+ "value": "{{ formatDate(data.modified, 'DD/MM/YYYY HH:mm') }}"
72
+ },
73
+ {
74
+ "title": "Modified by",
75
+ "value": "{{ data.modifiedBy || '-' }}"
76
+ }
77
+ ]
78
+ }
79
+ ]
80
+ },
81
+ {
82
+ "type": "Column",
83
+ "width": 4,
84
+ "items": [
85
+ {
86
+ "type": "ActionSet",
87
+ "actions": [
88
+ {
89
+ "showWhen": "data.status === 'STARTED'",
90
+ "type": "Action.PushCard",
91
+ "title": "Stop task",
92
+ "stateMachineName": "schedule_stopTask_1_0",
93
+ "input": {
94
+ "key": "$.key"
95
+ }
96
+ },
97
+ {
98
+ "showWhen": "data.status !== 'STARTED'",
99
+ "type": "Action.PushCard",
100
+ "title": "Start task",
101
+ "stateMachineName": "schedule_startTask_1_0",
102
+ "input": {
103
+ "key": "$.key"
104
+ }
105
+ },
106
+ {
107
+ "type": "Action.PushCard",
108
+ "title": "Modify schedule",
109
+ "stateMachineName": "schedule_modifyScheduledTask_1_0",
110
+ "input": {
111
+ "key": "$.key"
112
+ }
113
+ }
114
+ ]
115
+ }
116
+ ]
117
+ }
118
+ ]
119
+ },
120
+ {
121
+ "title": "State machine executions",
122
+ "type": "Table",
123
+ "arrayPath": "data.executions",
124
+ "columns": [
125
+ {
126
+ "title": "Execution name",
127
+ "field": "executionName"
128
+ },
129
+ {
130
+ "title": "Status",
131
+ "field": "status"
132
+ },
133
+ {
134
+ "title": "Created",
135
+ "field": "created",
136
+ "format": "formatDate(val, 'DD/MM/YYYY HH:mm')"
137
+ },
138
+ {
139
+ "title": "Modified",
140
+ "field": "modified",
141
+ "format": "formatDate(val, 'DD/MM/YYYY HH:mm')"
142
+ }
143
+ ]
144
+ }
145
+ ],
146
+ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
147
+ "version": "1.0"
148
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "label": "Schedule",
3
+ "description": "Scheduled state machines",
4
+ "style": {
5
+ "icon": "schedule",
6
+ "backgroundColor": "#008800"
7
+ }
8
+ }
@@ -0,0 +1,16 @@
1
+ const capitalise = str => str.charAt(0).toUpperCase() + str.slice(1)
2
+ const schema = 'schedule'
3
+
4
+ module.exports = function () {
5
+ return function (event, env) {
6
+ const now = env.bootedServices.timestamp.now()
7
+
8
+ const key = [
9
+ schema,
10
+ event.trim().split(' ').map((n, i) => i === 0 ? n : capitalise(n)).join(''),
11
+ now.format('YYYYMMDD-HHmm')
12
+ ].join('_')
13
+
14
+ return { key }
15
+ }
16
+ }
@@ -0,0 +1,18 @@
1
+ module.exports = function () {
2
+ return async function getAvailableStateMachines (event, env) {
3
+ const availableStateMachines = env.bootedServices.statebox
4
+ .listStateMachines()
5
+ .map(({ name }) => {
6
+ return {
7
+ title: name,
8
+ value: name
9
+ }
10
+ })
11
+
12
+ return {
13
+ availableStateMachines,
14
+ availableStateMachinesOrig: availableStateMachines,
15
+ availableStateMachinesFilt: availableStateMachines
16
+ }
17
+ }
18
+ }
@@ -35,20 +35,6 @@
35
35
  "startOnBoot": {
36
36
  "type": "boolean"
37
37
  },
38
- "lastRanStart": {
39
- "type": "string",
40
- "format": "date-time"
41
- },
42
- "lastRanEnd": {
43
- "type": "string",
44
- "format": "date-time"
45
- },
46
- "lastRanExecutionName": {
47
- "type": "string"
48
- },
49
- "totalRunCount": {
50
- "type": "integer"
51
- },
52
38
  "status": {
53
39
  "type": "string"
54
40
  }
@@ -0,0 +1,66 @@
1
+ {
2
+ "Comment": "Set up a Scheduled State Machine",
3
+ "name": "Set up a Scheduled State Machine",
4
+ "version": "1.0",
5
+ "categories": [
6
+ "schedule"
7
+ ],
8
+ "instigators": [
9
+ "user"
10
+ ],
11
+ "instigatorGroup": "form",
12
+ "StartAt": "GetStateMachines",
13
+ "States": {
14
+ "GetStateMachines": {
15
+ "Type": "Task",
16
+ "Resource": "function:schedule_getAvailableStateMachines",
17
+ "ResultPath": "$",
18
+ "Next": "DefaultInput"
19
+ },
20
+ "DefaultInput": {
21
+ "Type": "Pass",
22
+ "ResultSelector": {
23
+ "inputStr": "{}"
24
+ },
25
+ "Next": "View"
26
+ },
27
+ "View": {
28
+ "Type": "Task",
29
+ "Resource": "module:awaitingHumanInput",
30
+ "ResourceConfig": {
31
+ "uiType": "app",
32
+ "uiName": "schedule_createScheduledTask",
33
+ "dataPath": "$"
34
+ },
35
+ "Next": "GenerateKey"
36
+ },
37
+ "GenerateKey": {
38
+ "Type": "Task",
39
+ "InputPath": "$.name",
40
+ "Resource": "function:schedule_generateKey",
41
+ "ResultPath": "$",
42
+ "Next": "CreateScheduledTask"
43
+ },
44
+ "CreateScheduledTask": {
45
+ "Type": "Task",
46
+ "Parameters": {
47
+ "key.$": "$.key",
48
+ "scheduleType.$": "$.scheduleType",
49
+ "datetime.$": "$.datetime",
50
+ "interval.$": "$.interval",
51
+ "stateMachineName.$": "$.stateMachineName",
52
+ "input.$": "$.inputStr"
53
+ },
54
+ "Resource": "module:createScheduledTask",
55
+ "End": true
56
+ }
57
+ },
58
+ "restrictions": [
59
+ {
60
+ "roleId": "schedule_scheduleAdmin",
61
+ "allows": [
62
+ "*"
63
+ ]
64
+ }
65
+ ]
66
+ }
@@ -0,0 +1,74 @@
1
+ {
2
+ "Comment": "Modify a Scheduled State Machine",
3
+ "name": "Modify a Scheduled State Machine",
4
+ "version": "1.0",
5
+ "categories": [
6
+ "schedule"
7
+ ],
8
+ "StartAt": "SetAsEditing",
9
+ "States": {
10
+ "SetAsEditing": {
11
+ "Type": "Pass",
12
+ "Result": {
13
+ "editing": true
14
+ },
15
+ "ResultPath": "$",
16
+ "Next": "FindTask"
17
+ },
18
+ "FindTask": {
19
+ "Type": "Task",
20
+ "InputPath": "$.key",
21
+ "Resource": "module:findingById",
22
+ "ResourceConfig": {
23
+ "modelId": "schedule_task"
24
+ },
25
+ "ResultPath": "$",
26
+ "Next": "GetStateMachines"
27
+ },
28
+ "GetStateMachines": {
29
+ "Type": "Task",
30
+ "Resource": "function:schedule_getAvailableStateMachines",
31
+ "ResultPath": "$",
32
+ "Next": "View"
33
+ },
34
+ "View": {
35
+ "Type": "Task",
36
+ "Resource": "module:awaitingHumanInput",
37
+ "ResourceConfig": {
38
+ "uiType": "app",
39
+ "uiName": "schedule_createScheduledTask",
40
+ "dataPath": "$"
41
+ },
42
+ "Next": "UpdateScheduledTask"
43
+ },
44
+ "UpdateScheduledTask": {
45
+ "Type": "Task",
46
+ "Parameters": {
47
+ "key.$": "$.key",
48
+ "scheduleType.$": "$.scheduleType",
49
+ "datetime.$": "$.datetime",
50
+ "interval.$": "$.interval"
51
+ },
52
+ "Resource": "module:updateScheduledTask",
53
+ "Next": "UpdateScheduledStateMachineConfig"
54
+ },
55
+ "UpdateScheduledStateMachineConfig": {
56
+ "Type": "Task",
57
+ "Parameters": {
58
+ "key.$": "$.key",
59
+ "stateMachineName.$": "$.stateMachineName",
60
+ "input.$": "$.inputStr"
61
+ },
62
+ "Resource": "module:updateScheduledStateMachineConfig",
63
+ "End": true
64
+ }
65
+ },
66
+ "restrictions": [
67
+ {
68
+ "roleId": "schedule_scheduleAdmin",
69
+ "allows": [
70
+ "*"
71
+ ]
72
+ }
73
+ ]
74
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "Comment": "Start Scheduled State Machine",
3
+ "name": "Start Scheduled State Machine",
4
+ "version": "1.0",
5
+ "categories": [
6
+ "schedule"
7
+ ],
8
+ "StartAt": "StartScheduledTask",
9
+ "States": {
10
+ "StartScheduledTask": {
11
+ "Type": "Task",
12
+ "InputPath": "$.key",
13
+ "Resource": "module:startScheduledTask",
14
+ "Next": "View"
15
+ },
16
+ "View": {
17
+ "Type": "Task",
18
+ "Parameters": {
19
+ "key.$": "$.key"
20
+ },
21
+ "Resource": "module:runStateMachine",
22
+ "ResourceConfig": {
23
+ "stateMachine": "schedule_viewScheduledTask_1_0"
24
+ },
25
+ "End": true
26
+ }
27
+ },
28
+ "restrictions": [
29
+ {
30
+ "roleId": "schedule_scheduleAdmin",
31
+ "allows": [
32
+ "*"
33
+ ]
34
+ }
35
+ ]
36
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "Comment": "Stop Scheduled State Machine",
3
+ "name": "Stop Scheduled State Machine",
4
+ "version": "1.0",
5
+ "categories": [
6
+ "schedule"
7
+ ],
8
+ "StartAt": "StopScheduledTask",
9
+ "States": {
10
+ "StopScheduledTask": {
11
+ "Type": "Task",
12
+ "InputPath": "$.key",
13
+ "Resource": "module:stopScheduledTask",
14
+ "Next": "View"
15
+ },
16
+ "View": {
17
+ "Type": "Task",
18
+ "Parameters": {
19
+ "key.$": "$.key"
20
+ },
21
+ "Resource": "module:runStateMachine",
22
+ "ResourceConfig": {
23
+ "stateMachine": "schedule_viewScheduledTask_1_0"
24
+ },
25
+ "End": true
26
+ }
27
+ },
28
+ "restrictions": [
29
+ {
30
+ "roleId": "schedule_scheduleAdmin",
31
+ "allows": [
32
+ "*"
33
+ ]
34
+ }
35
+ ]
36
+ }
@@ -0,0 +1,69 @@
1
+ {
2
+ "Comment": "View Scheduled State Machines",
3
+ "name": "View Scheduled State Machines",
4
+ "version": "1.0",
5
+ "categories": [
6
+ "schedule"
7
+ ],
8
+ "instigators": [
9
+ "user"
10
+ ],
11
+ "instigatorGroup": "app",
12
+ "StartAt": "FindTasks",
13
+ "States": {
14
+ "FindTasks": {
15
+ "Type": "Task",
16
+ "Resource": "module:finding",
17
+ "ResourceConfig": {
18
+ "modelId": "schedule_task"
19
+ },
20
+ "ResultPath": "$.tasks",
21
+ "Next": "AddTaskLaunches"
22
+ },
23
+ "AddTaskLaunches": {
24
+ "Type": "Map",
25
+ "ItemsPath": "$.tasks",
26
+ "Iterator": {
27
+ "StartAt": "AddLaunches",
28
+ "States": {
29
+ "AddLaunches": {
30
+ "Type": "Pass",
31
+ "Parameters": {
32
+ "launches": [
33
+ {
34
+ "title": "View",
35
+ "stateMachineName": "schedule_viewScheduledTask",
36
+ "input": {
37
+ "key.$": "$.key"
38
+ }
39
+ }
40
+ ]
41
+ },
42
+ "ResultPath": "$",
43
+ "End": true
44
+ }
45
+ }
46
+ },
47
+ "ResultPath": "$.tasks",
48
+ "Next": "View"
49
+ },
50
+ "View": {
51
+ "Type": "Task",
52
+ "Resource": "module:awaitingHumanInput",
53
+ "ResourceConfig": {
54
+ "uiType": "app",
55
+ "uiName": "schedule_viewAllScheduledTasks",
56
+ "dataPath": "$"
57
+ },
58
+ "End": true
59
+ }
60
+ },
61
+ "restrictions": [
62
+ {
63
+ "roleId": "schedule_scheduleAdmin",
64
+ "allows": [
65
+ "*"
66
+ ]
67
+ }
68
+ ]
69
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "Comment": "View Scheduled State Machine",
3
+ "name": "View Scheduled State Machine",
4
+ "version": "1.0",
5
+ "categories": [
6
+ "schedule"
7
+ ],
8
+ "StartAt": "FindTask",
9
+ "States": {
10
+ "FindTask": {
11
+ "Type": "Task",
12
+ "InputPath": "$.key",
13
+ "Resource": "module:findingById",
14
+ "ResourceConfig": {
15
+ "modelId": "schedule_task"
16
+ },
17
+ "ResultPath": "$",
18
+ "Next": "FindExecutions"
19
+ },
20
+ "FindExecutions": {
21
+ "Type": "Task",
22
+ "InputPath": "$.key",
23
+ "Resource": "module:findScheduledExecutions",
24
+ "ResultPath": "$.executions",
25
+ "Next": "View"
26
+ },
27
+ "View": {
28
+ "Type": "Task",
29
+ "Resource": "module:awaitingHumanInput",
30
+ "ResourceConfig": {
31
+ "uiType": "app",
32
+ "uiName": "schedule_viewScheduledTask",
33
+ "dataPath": "$"
34
+ },
35
+ "End": true
36
+ }
37
+ },
38
+ "restrictions": [
39
+ {
40
+ "roleId": "schedule_scheduleAdmin",
41
+ "allows": [
42
+ "*"
43
+ ]
44
+ }
45
+ ]
46
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "label": "Schedule Admin",
3
+ "description": "Maintain scheduled state machines"
4
+ }
@@ -1,3 +1,5 @@
1
+ const INSTIGATING_CLIENT_DOMAIN = 'tymly-schedule-plugin'
2
+
1
3
  const nodeSchedule = require('node-schedule')
2
4
  const ruleToDatetime = require('./utils/rule-to-datetime')
3
5
  const datetimeToRule = require('./utils/datetime-to-rule')
@@ -6,6 +8,9 @@ class CronService {
6
8
  async boot (options) {
7
9
  this.statebox = options.bootedServices.statebox
8
10
  this.taskModel = options.bootedServices.storage.models.schedule_task
11
+ this.executionModel = options.bootedServices.storage.models.tymly_execution
12
+
13
+ // todo: rename blueprint component to scheduled-tasks, blueprint component to have "type": "stateMachineName"
9
14
  this.scheduledTasks = await this.findTasks(options.blueprintComponents.stateMachineSchedules)
10
15
 
11
16
  for (const [key, task] of Object.entries(this.scheduledTasks)) {
@@ -54,11 +59,11 @@ class CronService {
54
59
  jobOptions,
55
60
  () => {
56
61
  console.log(`Running scheduled state machine ${stateMachineName}`)
57
- const startedAt = new Date()
58
62
  const executionOptions = {
59
63
  instigatingClient: {
60
64
  appName: 'tymly',
61
- domain: 'tymly-schedule-plugin'
65
+ domain: INSTIGATING_CLIENT_DOMAIN,
66
+ scheduleKey: key
62
67
  },
63
68
  sendResponse
64
69
  }
@@ -66,7 +71,6 @@ class CronService {
66
71
  if (err) {
67
72
  console.log(`There was an error on execution of scheduled state machine ${stateMachineName}`)
68
73
  }
69
- await this.updateTaskOnExecution(key, executionDescription.executionName, startedAt)
70
74
  }
71
75
  this.statebox.startExecution(input, stateMachineName, executionOptions, cb)
72
76
  }
@@ -74,7 +78,7 @@ class CronService {
74
78
 
75
79
  this.scheduledTasks[key] = task
76
80
 
77
- return this.updateTaskStatus(key, 'STARTED')
81
+ return this.updateTaskStatus(key, task.job === null ? 'STOPPED' : 'STARTED')
78
82
  } // startTask
79
83
 
80
84
  stopTask (key) {
@@ -105,7 +109,7 @@ class CronService {
105
109
  setMissingPropertiesToNull: false
106
110
  }
107
111
  )
108
- }
112
+ } // updateTaskStatus
109
113
 
110
114
  stopTasks () {
111
115
  return Promise.all(
@@ -129,6 +133,11 @@ class CronService {
129
133
  continue
130
134
  }
131
135
 
136
+ const stateMachineOk = this.checkIfStateMachineExists(config.stateMachineName)
137
+ if (!stateMachineOk) {
138
+ continue
139
+ }
140
+
132
141
  // todo: recurrence rules?
133
142
 
134
143
  if (typeof config.interval === 'string') {
@@ -140,6 +149,10 @@ class CronService {
140
149
  config.interval = null
141
150
  }
142
151
 
152
+ config.input = typeof config.input === 'object'
153
+ ? config.input
154
+ : {}
155
+
143
156
  tasks[key] = config
144
157
 
145
158
  await this.persistTask(key, config)
@@ -158,35 +171,44 @@ class CronService {
158
171
  interval: config.interval,
159
172
  datetime: config.datetime,
160
173
  startOnBoot: config.startOnBoot,
161
- lastRanStart: null,
162
- lastRanEnd: null,
163
- lastRanExecutionName: null,
164
- totalRunCount: 0,
165
174
  status: null
166
175
  })
167
- }
176
+ } // persistTask
168
177
 
169
- async updateTaskOnExecution (key, executionName, startedAt) {
170
- const { totalRunCount } = await this.taskModel.findOne(
171
- {
172
- where: { key: { equals: key } },
173
- fields: ['totalRunCount']
174
- }
175
- )
178
+ checkIfStateMachineExists (stateMachineName) {
179
+ const [stateMachineOk, errStateMachineDesc] = this.statebox.checkIfStateMachineExists(stateMachineName)
180
+
181
+ if (!stateMachineOk) {
182
+ console.log('State machine error', errStateMachineDesc)
183
+ }
184
+
185
+ return stateMachineOk
186
+ } // checkIfStateMachineExists
187
+
188
+ async stopAndUpdateStateMachineConfig (key, stateMachineName, input) {
189
+ await this.stopTask(key)
190
+
191
+ const stateMachineOk = this.checkIfStateMachineExists(stateMachineName)
192
+ if (!stateMachineOk) {
193
+ return
194
+ }
195
+
196
+ this.scheduledTasks[key].stateMachineName = stateMachineName
197
+ this.scheduledTasks[key].input = typeof input === 'object'
198
+ ? input
199
+ : {}
176
200
 
177
201
  return this.taskModel.update(
178
202
  {
179
203
  key,
180
- lastRanStart: startedAt,
181
- lastRanEnd: new Date(),
182
- lastRanExecutionName: executionName,
183
- totalRunCount: totalRunCount + 1
204
+ stateMachineName: this.scheduledTasks[key].stateMachineName,
205
+ input: this.scheduledTasks[key].input
184
206
  },
185
207
  {
186
208
  setMissingPropertiesToNull: false
187
209
  }
188
210
  )
189
- }
211
+ } // stopAndUpdateStateMachineConfig
190
212
 
191
213
  async stopAndUpdateTaskSchedule (key, rule) {
192
214
  await this.stopTask(key)
@@ -212,7 +234,39 @@ class CronService {
212
234
  setMissingPropertiesToNull: false
213
235
  }
214
236
  )
215
- }
237
+ } // stopAndUpdateTaskSchedule
238
+
239
+ async findScheduledExecutions (key) {
240
+ const executions = await this.executionModel.find({
241
+ fields: [
242
+ 'executionName',
243
+ 'executionOptions',
244
+ 'status',
245
+ 'created',
246
+ 'modified'
247
+ ],
248
+ where: {
249
+ stateMachineName: {
250
+ equals: this.scheduledTasks[key].stateMachineName
251
+ }
252
+ },
253
+ orderBy: ['-modified']
254
+ })
255
+
256
+ return executions.filter(({ executionOptions }) => {
257
+ executionOptions = typeof executionOptions === 'string'
258
+ ? JSON.parse(executionOptions)
259
+ : executionOptions
260
+
261
+ return executionOptions.instigatingClient.domain === INSTIGATING_CLIENT_DOMAIN &&
262
+ executionOptions.instigatingClient.scheduleKey === key
263
+ })
264
+ } // findScheduledExecutions
265
+
266
+ createScheduledTask (key, config = {}) {
267
+ this.scheduledTasks[key] = config
268
+ return this.persistTask(key, config)
269
+ } // createScheduledTask
216
270
 
217
271
  async shutdown () {
218
272
  await nodeSchedule.gracefulShutdown()
@@ -0,0 +1,8 @@
1
+ {
2
+ "CreateScheduledTask": {
3
+ "Type": "Task",
4
+ "InputPath": "$.key",
5
+ "Resource": "module:createScheduledTask",
6
+ "End": true
7
+ }
8
+ }
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ description: 'Create task',
3
+ example: require('./example.json')
4
+ }
@@ -0,0 +1,56 @@
1
+ // const datetimeToRule = require('../../services/schedule/utils/datetime-to-rule')
2
+
3
+ class CreateScheduledTask {
4
+ init (resourceConfig, env) {
5
+ this.services = env.bootedServices
6
+ }
7
+
8
+ async run (event, context) {
9
+ const {
10
+ key,
11
+ stateMachineName,
12
+ scheduleType,
13
+ datetime,
14
+ interval
15
+ } = event
16
+
17
+ const stateMachineOk = this.services.schedule.checkIfStateMachineExists(stateMachineName)
18
+ if (!stateMachineOk) {
19
+ return context.sendTaskSuccess()
20
+ }
21
+
22
+ let input = typeof event.input === 'object'
23
+ ? event.input
24
+ : {}
25
+
26
+ if (typeof event.input === 'string') {
27
+ try {
28
+ input = JSON.parse(event.input)
29
+ } catch (err) {
30
+ console.log('Issue with input', event.input)
31
+ }
32
+ }
33
+
34
+ // const rule = scheduleType === 'interval'
35
+ // ? interval
36
+ // : datetimeToRule(new Date(datetime))
37
+
38
+ const config = {
39
+ stateMachineName,
40
+ input,
41
+ sendResponse: 'COMPLETE',
42
+ scheduleType,
43
+ interval,
44
+ datetime,
45
+ startOnBoot: false // todo
46
+ }
47
+
48
+ await this.services.schedule.createScheduledTask(key, config)
49
+ // await this.services.schedule.stopAndUpdateTaskSchedule(key, rule)
50
+ // await this.services.schedule.stopAndUpdateStateMachineConfig(key, stateMachineName, input)
51
+
52
+ return context.sendTaskSuccess()
53
+ }
54
+ }
55
+
56
+ module.exports = CreateScheduledTask
@@ -0,0 +1,8 @@
1
+ {
2
+ "FindScheduledExecutions": {
3
+ "Type": "Task",
4
+ "InputPath": "$.key",
5
+ "Resource": "module:findScheduledExecutions",
6
+ "End": true
7
+ }
8
+ }
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ description: 'Find scheduled executions',
3
+ example: require('./example.json')
4
+ }
@@ -0,0 +1,12 @@
1
+ class FindScheduledExecutions {
2
+ init (resourceConfig, env) {
3
+ this.services = env.bootedServices
4
+ }
5
+
6
+ async run (key, context) {
7
+ const executions = await this.services.schedule.findScheduledExecutions(key)
8
+ return context.sendTaskSuccess(executions)
9
+ }
10
+ }
11
+
12
+ module.exports = FindScheduledExecutions
@@ -0,0 +1,8 @@
1
+ {
2
+ "StartScheduledTask": {
3
+ "Type": "Task",
4
+ "InputPath": "$.key",
5
+ "Resource": "module:startScheduledTask",
6
+ "End": true
7
+ }
8
+ }
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ description: 'Start task',
3
+ example: require('./example.json')
4
+ }
@@ -0,0 +1,12 @@
1
+ class StartScheduledTask {
2
+ init (resourceConfig, env) {
3
+ this.services = env.bootedServices
4
+ }
5
+
6
+ async run (key, context) {
7
+ await this.services.schedule.startTask(key)
8
+ return context.sendTaskSuccess()
9
+ }
10
+ }
11
+
12
+ module.exports = StartScheduledTask
@@ -0,0 +1,8 @@
1
+ {
2
+ "StopScheduledTask": {
3
+ "Type": "Task",
4
+ "InputPath": "$.key",
5
+ "Resource": "module:stopScheduledTask",
6
+ "End": true
7
+ }
8
+ }
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ description: 'Stop task',
3
+ example: require('./example.json')
4
+ }
@@ -0,0 +1,12 @@
1
+ class StopScheduledTask {
2
+ init (resourceConfig, env) {
3
+ this.services = env.bootedServices
4
+ }
5
+
6
+ async run (key, context) {
7
+ await this.services.schedule.stopTask(key)
8
+ return context.sendTaskSuccess()
9
+ }
10
+ }
11
+
12
+ module.exports = StopScheduledTask
@@ -0,0 +1,12 @@
1
+ {
2
+ "UpdateScheduledStateMachineConfig": {
3
+ "Type": "Task",
4
+ "Parameters": {
5
+ "key.$": "$.key",
6
+ "stateMachineName.$": "$.stateMachineName",
7
+ "input.$": "$.input"
8
+ },
9
+ "Resource": "module:updateScheduledStateMachineConfig",
10
+ "End": true
11
+ }
12
+ }
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ description: 'Update state machine config',
3
+ example: require('./example.json')
4
+ }
@@ -0,0 +1,35 @@
1
+ class UpdateScheduledStateMachineConfig {
2
+ init (resourceConfig, env) {
3
+ this.services = env.bootedServices
4
+ }
5
+
6
+ async run (event, context) {
7
+ const {
8
+ key,
9
+ stateMachineName
10
+ } = event
11
+
12
+ const stateMachineOk = this.services.schedule.checkIfStateMachineExists(stateMachineName)
13
+ if (!stateMachineOk) {
14
+ return context.sendTaskSuccess()
15
+ }
16
+
17
+ let input = typeof event.input === 'object'
18
+ ? event.input
19
+ : {}
20
+
21
+ if (typeof event.input === 'string') {
22
+ try {
23
+ input = JSON.parse(event.input)
24
+ } catch (err) {
25
+ console.log('Issue with input', event.input)
26
+ }
27
+ }
28
+
29
+ await this.services.schedule.stopAndUpdateStateMachineConfig(key, stateMachineName, input)
30
+
31
+ return context.sendTaskSuccess()
32
+ }
33
+ }
34
+
35
+ module.exports = UpdateScheduledStateMachineConfig
@@ -0,0 +1,13 @@
1
+ {
2
+ "UpdateScheduledTask": {
3
+ "Type": "Task",
4
+ "Parameters": {
5
+ "key.$": "$.key",
6
+ "scheduleType.$": "$.scheduleType",
7
+ "datetime.$": "$.datetime",
8
+ "interval.$": "$.interval"
9
+ },
10
+ "Resource": "module:updateScheduledTask",
11
+ "End": true
12
+ }
13
+ }
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ description: 'Update task',
3
+ example: require('./example.json')
4
+ }
@@ -0,0 +1,26 @@
1
+ const datetimeToRule = require('../../services/schedule/utils/datetime-to-rule')
2
+
3
+ class UpdateScheduledTask {
4
+ init (resourceConfig, env) {
5
+ this.services = env.bootedServices
6
+ }
7
+
8
+ async run (event, context) {
9
+ const {
10
+ key,
11
+ scheduleType,
12
+ datetime,
13
+ interval
14
+ } = event
15
+
16
+ const rule = scheduleType === 'interval'
17
+ ? interval
18
+ : datetimeToRule(new Date(datetime))
19
+
20
+ await this.services.schedule.stopAndUpdateTaskSchedule(key, rule)
21
+
22
+ return context.sendTaskSuccess()
23
+ }
24
+ }
25
+
26
+ module.exports = UpdateScheduledTask
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wmfs/tymly-schedule-plugin",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Provides state machine scheduling for the Tymly framework",
5
5
  "homepage": "https://github.com/wmfs/tymly-schedule-plugin#readme",
6
6
  "author": "West Midlands Fire Service",
@@ -30,6 +30,7 @@
30
30
  "@semantic-release/git": "10.0.1",
31
31
  "@wmfs/tymly": "1.235.1",
32
32
  "@wmfs/tymly-pg-plugin": "1.268.0",
33
+ "@wmfs/tymly-test-helpers": "1.16.0",
33
34
  "chai": "4.3.6",
34
35
  "codecov": "3.8.3",
35
36
  "conventional-changelog-metahub": "4.0.1",
package/plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "schedule-plugin",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "label": "Tymly Schedule Plugin",
5
5
  "author": "WMFS",
6
6
  "organisation": "West Midlands Fire Service",
@@ -18,9 +18,7 @@ describe('Tymly schedule tests', function () {
18
18
 
19
19
  let tymlyService
20
20
  let scheduleService
21
- // let statebox
22
21
  let catStatsModel
23
- // let executionsModel
24
22
  let taskModel
25
23
  let client
26
24
 
@@ -34,17 +32,16 @@ describe('Tymly schedule tests', function () {
34
32
  ],
35
33
  pluginPaths: [
36
34
  path.resolve(__dirname, '..'),
37
- require.resolve('@wmfs/tymly-pg-plugin')
35
+ require.resolve('@wmfs/tymly-pg-plugin'),
36
+ require.resolve('@wmfs/tymly-test-helpers/plugins/mock-users-plugin')
38
37
  ]
39
38
  }
40
39
  )
41
40
 
42
41
  tymlyService = tymlyServices.tymly
43
42
  scheduleService = tymlyServices.schedule
44
- // statebox = tymlyServices.statebox
45
43
  taskModel = tymlyServices.storage.models.schedule_task
46
44
  catStatsModel = tymlyServices.storage.models.tymlyTest_catStats
47
- // executionsModel = tymlyServices.storage.models.tymly_execution
48
45
  client = tymlyServices.storage.client
49
46
 
50
47
  const catStats = await catStatsModel.find({})
@@ -74,9 +71,8 @@ describe('Tymly schedule tests', function () {
74
71
  expect(tasks[0].datetime).to.eql(null)
75
72
  expect(tasks[0].status).to.eql('STOPPED')
76
73
 
77
- const { totalRunCount } = tasks[0]
78
-
79
- lastRunCount = totalRunCount
74
+ const executions = await scheduleService.findScheduledExecutions(scheduleKey)
75
+ lastRunCount = executions.length
80
76
 
81
77
  // Waited 10 seconds and runs every 2 seconds so can be 5 or 6
82
78
  expect(lastRunCount).to.be.greaterThanOrEqual(5)
@@ -96,7 +92,8 @@ describe('Tymly schedule tests', function () {
96
92
  expect(tasks[0].datetime).to.eql(null)
97
93
  expect(tasks[0].status).to.eql('STOPPED')
98
94
 
99
- expect(tasks[0].totalRunCount).to.eql(lastRunCount)
95
+ const executions = await scheduleService.findScheduledExecutions(scheduleKey)
96
+ expect(executions.length).to.eql(lastRunCount)
100
97
 
101
98
  const catStats = await catStatsModel.find({})
102
99
  expect(catStats.length).to.eql(lastRunCount)
@@ -131,9 +128,11 @@ describe('Tymly schedule tests', function () {
131
128
  expect(tasks[0].interval).to.eql(null)
132
129
  expect(tasks[0].datetime).to.not.eql(null)
133
130
  expect(tasks[0].scheduleType).to.eql('datetime')
134
- expect(tasks[0].totalRunCount).to.eql(lastRunCount)
135
131
  expect(tasks[0].status).to.eql('STARTED')
136
132
 
133
+ const executions = await scheduleService.findScheduledExecutions(scheduleKey)
134
+ expect(executions.length).to.eql(lastRunCount)
135
+
137
136
  const catStats = await catStatsModel.find({})
138
137
  expect(catStats.length).to.eql(lastRunCount)
139
138
  })
@@ -145,7 +144,8 @@ describe('Tymly schedule tests', function () {
145
144
  expect(tasks.length).to.eql(1)
146
145
  expect(tasks[0].status).to.eql('STARTED')
147
146
 
148
- expect(tasks[0].totalRunCount).to.eql(lastRunCount)
147
+ const executions = await scheduleService.findScheduledExecutions(scheduleKey)
148
+ expect(executions.length).to.eql(lastRunCount)
149
149
 
150
150
  const catStats = await catStatsModel.find({})
151
151
  expect(catStats.length).to.eql(lastRunCount)
@@ -163,7 +163,8 @@ describe('Tymly schedule tests', function () {
163
163
  ],
164
164
  pluginPaths: [
165
165
  path.resolve(__dirname, '..'),
166
- require.resolve('@wmfs/tymly-pg-plugin')
166
+ require.resolve('@wmfs/tymly-pg-plugin'),
167
+ require.resolve('@wmfs/tymly-test-helpers/plugins/mock-users-plugin')
167
168
  ]
168
169
  }
169
170
  )
@@ -182,8 +183,10 @@ describe('Tymly schedule tests', function () {
182
183
  expect(tasks[0].stateMachineName).to.eql('tymlyTest_sendCatUpdate')
183
184
  expect(tasks[0].scheduleType).to.eql('datetime')
184
185
  expect(tasks[0].interval).to.eql(null)
185
- expect(tasks[0].status).to.eql('STARTED')
186
- expect(tasks[0].totalRunCount).to.eql(lastRunCount)
186
+ expect(tasks[0].status).to.eql('STOPPED')
187
+
188
+ const executions = await scheduleService.findScheduledExecutions(scheduleKey)
189
+ expect(executions.length).to.eql(lastRunCount)
187
190
  })
188
191
 
189
192
  it('clean up data', async () => {