@things-factory/scheduler 8.0.0-beta.1 → 8.0.0-beta.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/scheduler",
3
- "version": "8.0.0-beta.1",
3
+ "version": "8.0.0-beta.2",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "dist-client/index.js",
6
6
  "things-factory": true,
@@ -29,9 +29,9 @@
29
29
  "dependencies": {
30
30
  "@operato/graphql": "^8.0.0-beta",
31
31
  "@operato/shell": "^8.0.0-beta",
32
- "@things-factory/auth-base": "^8.0.0-beta.1",
33
- "@things-factory/scheduler-client": "^8.0.0-beta.1",
34
- "@things-factory/shell": "^8.0.0-beta.1"
32
+ "@things-factory/auth-base": "^8.0.0-beta.2",
33
+ "@things-factory/scheduler-client": "^8.0.0-beta.2",
34
+ "@things-factory/shell": "^8.0.0-beta.2"
35
35
  },
36
- "gitHead": "36c494e587640c1490318ef7b95adab02606e0c2"
36
+ "gitHead": "f03431a09435511b2595515658f9cb8f78ba4ebb"
37
37
  }
@@ -1 +0,0 @@
1
- export default function bootstrap() {}
package/client/index.ts DELETED
File without changes
@@ -1,75 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@operato/input/ox-input-crontab.js'
3
-
4
- import { css, html, LitElement } from 'lit'
5
- import { customElement, property } from 'lit/decorators.js'
6
-
7
- import { i18next } from '@operato/i18n'
8
- import { closePopup } from '@operato/popup'
9
- import { CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'
10
-
11
- @customElement('popup-crontab-input')
12
- export class PopupCrontabInput extends LitElement {
13
- static styles = [
14
- CommonHeaderStyles,
15
- ScrollbarStyles,
16
- css`
17
- :host {
18
- display: flex;
19
- flex-direction: column;
20
-
21
- background-color: var(--md-sys-color-surface);
22
-
23
- width: var(--overlay-center-normal-width, 50%);
24
- height: var(--overlay-center-normal-height, 50%);
25
- }
26
-
27
- ox-input-crontab {
28
- flex: 1;
29
- overflow-y: auto;
30
- }
31
-
32
- span {
33
- flex: 1;
34
-
35
- display: flex;
36
- align-items: center;
37
- justify-content: center;
38
-
39
- color: var(--md-sys-color-on-primary-container);
40
- }
41
- `
42
- ]
43
-
44
- @property({ type: Object }) value: any
45
- @property({ type: Object }) confirmCallback!: (newval: any) => void
46
-
47
- render() {
48
- return html`
49
- <ox-input-crontab .value=${this.value} @change=${this.onChange.bind(this)}> </ox-input-crontab>
50
-
51
- <div class="footer">
52
- <div filler></div>
53
- <button @click=${this.onCancel.bind(this)} danger>
54
- <md-icon>cancel</md-icon>${i18next.t('button.cancel')}
55
- </button>
56
- <button @click=${this.onConfirm.bind(this)} done><md-icon>done</md-icon>${i18next.t('button.confirm')}</button>
57
- </div>
58
- `
59
- }
60
-
61
- private onChange(e: CustomEvent) {
62
- e.stopPropagation()
63
-
64
- this.value = e.detail
65
- }
66
-
67
- private onCancel(e: Event) {
68
- closePopup(this)
69
- }
70
-
71
- private onConfirm(e: Event) {
72
- this.confirmCallback && this.confirmCallback(this.value)
73
- closePopup(this)
74
- }
75
- }
@@ -1,326 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@material/web/button/elevated-button.js'
3
- import '@operato/data-grist/ox-grist.js'
4
- import '@operato/data-grist/ox-filters-form.js'
5
- import '@operato/data-grist/ox-record-creator.js'
6
- import './schedule-popup.js'
7
-
8
- import { CommonHeaderStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
9
- import { PageView, store } from '@operato/shell'
10
- import { css, html } from 'lit'
11
- import { customElement, property, query } from 'lit/decorators.js'
12
- import { DataGrist, FetchOption } from '@operato/data-grist'
13
- import { client } from '@operato/graphql'
14
- import { i18next, localize } from '@operato/i18n'
15
- import { notify, openPopup } from '@operato/layout'
16
- import { OxPrompt } from '@operato/popup'
17
- import { isMobileDevice } from '@operato/utils'
18
-
19
- import { connect } from 'pwa-helpers/connect-mixin'
20
- import gql from 'graphql-tag'
21
-
22
- @customElement('schedule-list-page')
23
- export class SchedulerListPage extends connect(store)(localize(i18next)(PageView)) {
24
- static styles = [
25
- ScrollbarStyles,
26
- CommonGristStyles,
27
- CommonHeaderStyles,
28
- css`
29
- :host {
30
- display: flex;
31
-
32
- width: 100%;
33
-
34
- --grid-record-emphasized-background-color: #8b0000;
35
- --grid-record-emphasized-color: #ff6b6b;
36
- }
37
-
38
- ox-grist {
39
- overflow-y: auto;
40
- flex: 1;
41
- }
42
-
43
- ox-filters-form {
44
- flex: 1;
45
- }
46
- `
47
- ]
48
-
49
- @property({ type: Object }) gristConfig: any
50
- @property({ type: String }) mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
51
-
52
- @query('ox-grist') private grist!: DataGrist
53
-
54
- get context() {
55
- return {
56
- title: i18next.t('title.schedule list'),
57
- search: {
58
- handler: (search: string) => {
59
- this.grist.searchText = search
60
- },
61
- value: this.grist.searchText
62
- },
63
- filter: {
64
- handler: () => {
65
- this.grist.toggleHeadroom()
66
- }
67
- },
68
- help: 'scheduler/scheduler'
69
- }
70
- }
71
-
72
- render() {
73
- const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
74
-
75
- return html`
76
- <ox-grist .mode=${mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>
77
- <div slot="headroom" class="header">
78
- <div class="filters">
79
- <ox-filters-form autofocus without-search></ox-filters-form>
80
-
81
- <div id="modes">
82
- <md-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</md-icon>
83
- <md-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</md-icon>
84
- <md-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</md-icon>
85
- </div>
86
-
87
- <ox-record-creator id="add" .customPopupCallback=${this.popupCallback.bind(this)}>
88
- <button>
89
- <md-icon>add</md-icon>
90
- </button>
91
- <schedule-popup slot="popup"></schedule-popup>
92
- </ox-record-creator>
93
- </div>
94
- </div>
95
- </ox-grist>
96
- `
97
- }
98
-
99
- async pageInitialized(lifecycle: any) {
100
- this.gristConfig = {
101
- list: {
102
- fields: ['name', 'description'],
103
- details: ['active', 'updatedAt']
104
- },
105
- columns: [
106
- { type: 'gutter', gutterName: 'sequence' },
107
- { type: 'gutter', gutterName: 'row-selector', multiple: true },
108
- {
109
- type: 'gutter',
110
- gutterName: 'button',
111
- fixed: true,
112
- icon: record => (!record ? 'reorder' : record.id ? 'reorder' : ''),
113
- iconOnly: false,
114
- title: record => (!record ? i18next.t('button.detail') : record.id ? i18next.t('button.detail') : ''),
115
- width: 72,
116
- handlers: {
117
- click: (columns, data, column, record, rowIndex) => {
118
- if (!record.id) return
119
- openPopup(
120
- html`
121
- <schedule-popup schedule-id=${record.id} @requestRefresh=${() => this.grist.fetch()}></schedule-popup>
122
- `,
123
- {
124
- backdrop: true,
125
- help: 'scheduler/scheduler',
126
- size: 'large',
127
- title: i18next.t('title.schedule detail')
128
- }
129
- )
130
- }
131
- }
132
- },
133
- {
134
- type: 'gutter',
135
- gutterName: 'button',
136
- fixed: true,
137
- icon: record => (!record ? 'delete' : record.id ? 'delete' : ''),
138
- iconOnly: false,
139
- title: record => (!record ? i18next.t('button.delete') : record.id ? i18next.t('button.delete') : ''),
140
- width: 72,
141
- handlers: {
142
- click: (_columns, _data, _column, record, _rowIndex) => {
143
- if (record) this.unregisterSchedule(record.id)
144
- }
145
- }
146
- },
147
- {
148
- type: 'string',
149
- name: 'name',
150
- header: i18next.t('field.name'),
151
- record: {
152
- editable: false
153
- },
154
- filter: 'i_like' /* only i_like supported */,
155
- sortable: true,
156
- width: 150
157
- },
158
- {
159
- type: 'string',
160
- name: 'application',
161
- header: i18next.t('field.schedule-application'),
162
- record: {
163
- renderer: (value, column, record, rowIndex, field) => record.client?.application
164
- },
165
- filter: 'i_like' /* only i_like supported */,
166
- sortable: true,
167
- width: 150
168
- },
169
- {
170
- type: 'string',
171
- name: 'group',
172
- header: i18next.t('field.group'),
173
- record: {
174
- renderer: (value, column, record, rowIndex, field) => record.client?.group
175
- },
176
- filter: 'i_like' /* only i_like supported */,
177
- sortable: true,
178
- width: 150
179
- },
180
- {
181
- type: 'string',
182
- name: 'type',
183
- header: i18next.t('field.type'),
184
- record: {
185
- renderer: (value, column, record, rowIndex, field) => record.client?.type
186
- },
187
- filter: 'i_like' /* only i_like supported */,
188
- sortable: true,
189
- width: 150
190
- },
191
- {
192
- type: 'string',
193
- name: 'key',
194
- header: i18next.t('field.key'),
195
- record: {
196
- renderer: (value, column, record, rowIndex, field) => record.client?.key
197
- },
198
- filter: 'i_like' /* only i_like supported */,
199
- sortable: true,
200
- width: 150
201
- },
202
- {
203
- type: 'string',
204
- name: 'operation',
205
- header: i18next.t('field.schedule-operation'),
206
- record: {
207
- renderer: (value, column, record, rowIndex, field) => record.client?.operation
208
- },
209
- filter: 'i_like' /* only i_like supported */,
210
- sortable: true,
211
- width: 150
212
- },
213
- {
214
- type: 'datetime',
215
- name: 'updatedAt',
216
- header: i18next.t('field.updated_at'),
217
- record: {
218
- editable: false
219
- },
220
- sortable: true,
221
- width: 180
222
- }
223
- ],
224
- rows: {
225
- appendable: false,
226
- selectable: {
227
- multiple: true
228
- }
229
- },
230
- sorters: [
231
- {
232
- name: 'name'
233
- }
234
- ]
235
- }
236
- }
237
-
238
- async pageUpdated(changes: any, lifecycle: any) {
239
- if (this.active) {
240
- this.grist.fetch()
241
- }
242
- }
243
-
244
- async popupCallback(popup) {
245
- popup.onclosed = () => {}
246
- }
247
-
248
- async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
249
- const response = await client.query({
250
- query: gql`
251
- query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
252
- responses: schedules(filters: $filters, pagination: $pagination, sortings: $sortings) {
253
- items {
254
- id
255
- name
256
- client {
257
- application
258
- group
259
- type
260
- key
261
- operation
262
- }
263
- type
264
- schedule
265
- timezone
266
- task {
267
- type
268
- connection {
269
- host
270
- headers
271
- topic
272
- }
273
- data
274
- history_check
275
- failed_policy
276
- retry_wait
277
- retry_count
278
- max_retry_count
279
- }
280
- }
281
- total
282
- }
283
- }
284
- `,
285
- variables: {
286
- filters,
287
- pagination: { page, limit },
288
- sortings
289
- }
290
- })
291
-
292
- return {
293
- total: response.data.responses.total || 0,
294
- records: response.data.responses.items || []
295
- }
296
- }
297
-
298
- async unregisterSchedule(handle: string) {
299
- if (
300
- await OxPrompt.open({
301
- title: i18next.t('text.are_you_sure'),
302
- text: i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }),
303
- confirmButton: { text: i18next.t('button.confirm') },
304
- cancelButton: { text: i18next.t('button.cancel') }
305
- })
306
- ) {
307
- const response = await client.mutate({
308
- mutation: gql`
309
- mutation ($handle: ID!) {
310
- unregisterSchedule(handle: $handle)
311
- }
312
- `,
313
- variables: {
314
- handle
315
- }
316
- })
317
-
318
- if (!response.errors) {
319
- this.grist.fetch()
320
- notify({
321
- message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
322
- })
323
- }
324
- }
325
- }
326
- }
@@ -1,484 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@material/web/textfield/filled-text-field.js'
3
- import '@material/web/select/outlined-select.js'
4
- import '@material/web/checkbox/checkbox.js'
5
- import '@material/web/button/filled-button.js'
6
- import '@operato/input/ox-input-data.js'
7
- import '@operato/input/ox-input-key-values.js'
8
-
9
- import { html, css, LitElement, PropertyValues } from 'lit'
10
- import { customElement, property, query, state } from 'lit/decorators.js'
11
- import gql from 'graphql-tag'
12
-
13
- import { client } from '@operato/graphql'
14
- import { i18next } from '@operato/i18n'
15
- import { CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'
16
- import { notify, openPopup } from '@operato/layout'
17
- import moment from '@operato/moment-timezone-es'
18
-
19
- import { Schedule } from 'types'
20
-
21
- function getSystemTimeZone(): string {
22
- try {
23
- const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
24
- if (!timeZone) {
25
- throw new Error('Unable to resolve timeZone')
26
- }
27
- return timeZone
28
- } catch (e) {
29
- console.warn('Failed to get system timeZone, falling back to UTC.', e)
30
- return 'UTC'
31
- }
32
- }
33
-
34
- const DEFAULT_TZ = getSystemTimeZone()
35
- const TIMEZONE_OPTIONS = ['', DEFAULT_TZ, ...moment.tz.names().filter(tz => tz !== DEFAULT_TZ)]
36
-
37
- @customElement('schedule-popup')
38
- export class NewSchedulePopup extends LitElement {
39
- static styles = [
40
- ScrollbarStyles,
41
- CommonHeaderStyles,
42
- css`
43
- :host {
44
- display: flex;
45
- flex-direction: column;
46
- color: var(--popup-content-color);
47
- background-color: var(--popup-content-background-color);
48
- }
49
-
50
- [content] {
51
- flex: 1;
52
- padding: var(--spacing-medium);
53
- overflow: auto;
54
- }
55
-
56
- label {
57
- display: flex;
58
- flex-direction: row;
59
- gap: 10px;
60
- align-items: center;
61
- }
62
-
63
- md-filled-button,
64
- md-outlined-button {
65
- margin-top: 16px;
66
- }
67
-
68
- .footer {
69
- display: flex;
70
- align-items: center;
71
- justify-content: space-between;
72
- }
73
- `
74
- ]
75
-
76
- @property({ type: Object }) closeCallback?: (schedule?: Schedule | null) => void
77
- @property({ type: String, attribute: 'schedule-id' }) scheduleId?: string
78
-
79
- @state() schedule: Schedule = {}
80
-
81
- @query('form') form!: HTMLFormElement
82
-
83
- render() {
84
- const updatable = !!this.scheduleId
85
-
86
- return html`
87
- <div content>
88
- <form @change=${this.onInputChanged}>
89
- <md-filled-text-field
90
- label="Name"
91
- type="text"
92
- id="name"
93
- name="name"
94
- .value=${this.schedule.name || ''}
95
- required
96
- ></md-filled-text-field>
97
- <br /><br />
98
-
99
- <fieldset>
100
- <legend>Client Details</legend>
101
- <md-filled-text-field
102
- label="Application"
103
- type="text"
104
- id="application"
105
- name="client.application"
106
- .value=${this.schedule.client?.application || ''}
107
- ?readonly=${updatable}
108
- required
109
- ></md-filled-text-field>
110
- <br /><br />
111
-
112
- <md-filled-text-field
113
- label="Group"
114
- type="text"
115
- id="group"
116
- name="client.group"
117
- .value=${this.schedule.client?.group || ''}
118
- ?readonly=${updatable}
119
- required
120
- ></md-filled-text-field>
121
- <br /><br />
122
-
123
- <md-filled-text-field
124
- label="Type"
125
- type="text"
126
- id="type"
127
- name="client.type"
128
- .value=${this.schedule.client?.type || ''}
129
- ?readonly=${updatable}
130
- required
131
- ></md-filled-text-field>
132
- <br /><br />
133
-
134
- <md-filled-text-field
135
- label="Key"
136
- type="text"
137
- id="key"
138
- name="client.key"
139
- .value=${this.schedule.client?.key || ''}
140
- ?readonly=${updatable}
141
- required
142
- ></md-filled-text-field>
143
- <br /><br />
144
-
145
- <md-filled-text-field
146
- label="Operation"
147
- type="text"
148
- id="operation"
149
- name="client.operation"
150
- .value=${this.schedule.client?.operation || ''}
151
- ?readonly=${updatable}
152
- required
153
- ></md-filled-text-field>
154
- </fieldset>
155
- <br />
156
-
157
- <fieldset>
158
- <legend>Schedule</legend>
159
-
160
- <md-outlined-select label="Schedule Type" name="type" required>
161
- <md-select-option value="now" ?selected=${this.schedule.type === 'now'}>Now</md-select-option>
162
- <md-select-option value="delay" ?selected=${this.schedule.type === 'delay'}>Delay</md-select-option>
163
- <md-select-option value="date" ?selected=${this.schedule.type === 'date'}>Date</md-select-option>
164
- <md-select-option value="cron" ?selected=${this.schedule.type === 'cron'}>Cron</md-select-option>
165
- <md-select-option value="delay-recur" ?selected=${this.schedule.type === 'delay-recur'}
166
- >Delay-Recur</md-select-option
167
- >
168
- </md-outlined-select>
169
- <br /><br />
170
-
171
- <md-filled-text-field
172
- label="Schedule (optional)"
173
- type="text"
174
- id="schedule"
175
- name="schedule"
176
- .value=${this.schedule.schedule || ''}
177
- @click=${(e: MouseEvent) => {
178
- const x = e.target as HTMLInputElement
179
- openPopup(
180
- html`
181
- <ox-popup-crontab-input
182
- .value=${x.value}
183
- .confirmCallback=${value => {
184
- x.value = value
185
- x.dispatchEvent(new Event('change', { bubbles: true, cancelable: true }))
186
- }}
187
- ></ox-popup-crontab-input>
188
- `,
189
- {
190
- backdrop: true,
191
- title: i18next.t('title.setting schedule')
192
- }
193
- )
194
- }}
195
- ></md-filled-text-field>
196
- <br /><br />
197
-
198
- <md-outlined-select
199
- label="Timezone (optional)"
200
- id="timezone"
201
- name="timezone"
202
- .value=${this.schedule.timezone || ''}
203
- required
204
- >
205
- ${TIMEZONE_OPTIONS.map(
206
- tz =>
207
- html` <md-select-option value=${tz} ?selected=${this.schedule.timezone === tz}
208
- >${tz}</md-select-option
209
- >`
210
- )}
211
- </md-outlined-select>
212
- </fieldset>
213
- <br /><br />
214
-
215
- <fieldset>
216
- <legend>Task Details</legend>
217
- <md-outlined-select label="Task Type" name="task.type" required>
218
- <md-select-option value="rest" ?selected=${this.schedule.task?.type === 'rest'}>REST</md-select-option>
219
- <md-select-option value="kafka" ?selected=${this.schedule.task?.type === 'kafka'}>Kafka</md-select-option>
220
- <md-select-option value="redis" ?selected=${this.schedule.task?.type === 'redis'}>Redis</md-select-option>
221
- </md-outlined-select>
222
- <br /><br />
223
-
224
- <md-filled-text-field
225
- label="Connection Host"
226
- id="connectionHost"
227
- name="task.connection.host"
228
- .value=${this.schedule.task?.connection?.host || ''}
229
- required
230
- ></md-filled-text-field>
231
- <br /><br />
232
-
233
- <md-filled-text-field
234
- label="Connection Topic (optional)"
235
- id="connectionTopic"
236
- name="task.connection.topic"
237
- .value=${this.schedule.task?.connection?.topic || ''}
238
- ></md-filled-text-field>
239
- <br /><br />
240
-
241
- <fieldset>
242
- <legend>Connection Headers (optional)</legend>
243
- <ox-input-key-values
244
- name="task.connection.headers"
245
- .value=${this.schedule.task?.connection?.headers || {}}
246
- ></ox-input-key-values>
247
- </fieldset>
248
- <br /><br />
249
-
250
- <fieldset>
251
- <legend>Data (JSON)</legend>
252
-
253
- <ox-input-data name="task.data" .value=${this.schedule.task?.data}> </ox-input-data>
254
- </fieldset>
255
- <br /><br />
256
-
257
- <label>
258
- <md-checkbox
259
- type="checkbox"
260
- id="historyCheck"
261
- name="task.history_check"
262
- .checked=${this.schedule.task?.history_check || false}
263
- ></md-checkbox
264
- >History Check
265
- </label>
266
- <br /><br />
267
-
268
- <md-outlined-select label="Failed Policy" name="task.failed_policy" required>
269
- <md-select-option value="ignore" ?selected=${this.schedule.task?.failed_policy === 'ignore'}
270
- >Ignore</md-select-option
271
- >
272
- <md-select-option value="retry" ?selected=${this.schedule.task?.failed_policy === 'retry'}
273
- >Retry</md-select-option
274
- >
275
- <md-select-option value="retry_dlq" ?selected=${this.schedule.task?.failed_policy === 'retry_dlq'}
276
- >Retry DLQ</md-select-option
277
- >
278
- </md-outlined-select>
279
- <br /><br />
280
-
281
- <md-filled-text-field
282
- label="Retry Wait (ms)"
283
- type="number"
284
- id="retryWait"
285
- name="task.retry_wait"
286
- .valueAsNumber=${this.schedule.task?.retry_wait || 0}
287
- ></md-filled-text-field>
288
- <br /><br />
289
-
290
- <md-filled-text-field
291
- label="Retry Count"
292
- type="number"
293
- id="retryCount"
294
- name="task.retry_count"
295
- .valueAsNumber=${this.schedule.task?.retry_count || 0}
296
- ></md-filled-text-field>
297
- <br /><br />
298
-
299
- <md-filled-text-field
300
- label="Max Retry Count"
301
- type="number"
302
- id="maxRetryCount"
303
- name="task.max_retry_count"
304
- .valueAsNumber=${this.schedule.task?.max_retry_count || 0}
305
- ></md-filled-text-field>
306
- </fieldset>
307
- </form>
308
- </div>
309
-
310
- <div class="footer">
311
- <button @click=${this.reset.bind(this)}><md-icon>restart_alt</md-icon>${i18next.t('button.reset')}</button>
312
- <div filler></div>
313
- <button @click=${this.cancel.bind(this)}><md-icon>cancel</md-icon>${i18next.t('button.cancel')}</button>
314
- ${updatable
315
- ? html` <button @click=${this.updateSchedule.bind(this)} done>
316
- <md-icon>update</md-icon>${i18next.t('button.update')}
317
- </button>`
318
- : html` <button @click=${this.registerSchedule.bind(this)} done>
319
- <md-icon>pending_actions</md-icon>${i18next.t('button.register')}
320
- </button>`}
321
- </div>
322
- `
323
- }
324
-
325
- async updated(changes: PropertyValues<this>) {
326
- if (changes.has('scheduleId')) {
327
- this.schedule = this.scheduleId ? await this.fetchSchedule(this.scheduleId) : {}
328
- }
329
- }
330
-
331
- private onInputChanged(event: Event) {
332
- const target = event.target as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
333
- const nameParts = target.name.split('.')
334
- let current = this.schedule
335
-
336
- for (let i = 0; i < nameParts.length - 1; i++) {
337
- const part = nameParts[i]
338
- if (!current[part]) {
339
- current[part] = {}
340
- }
341
- current = current[part]
342
- }
343
-
344
- const key = nameParts[nameParts.length - 1]
345
- current[key] =
346
- 'checked' in target
347
- ? (target as HTMLInputElement).checked
348
- : target.type === 'number'
349
- ? Number(target.value)
350
- : key == 'task.data'
351
- ? JSON.parse(target.value)
352
- : target.value
353
-
354
- this.requestUpdate()
355
- }
356
-
357
- private reset() {
358
- this.form.reset()
359
- }
360
-
361
- private cancel() {
362
- history.back()
363
- }
364
-
365
- private prepareSchedule() {
366
- this.schedule = {
367
- ...this.schedule,
368
- client: {
369
- application: this.schedule.client?.application || '',
370
- group: this.schedule.client?.group || '',
371
- type: this.schedule.client?.type || '',
372
- key: this.schedule.client?.key || '',
373
- operation: this.schedule.client?.operation || ''
374
- },
375
- task: {
376
- ...this.schedule.task,
377
- data: this.schedule.task?.data || {},
378
- history_check: this.schedule.task?.history_check ?? false,
379
- failed_policy: this.schedule.task?.failed_policy || 'ignore',
380
- retry_wait: this.schedule.task?.retry_wait || 0,
381
- retry_count: this.schedule.task?.retry_count || 0,
382
- max_retry_count: this.schedule.task?.max_retry_count || 0,
383
- connection: {
384
- ...this.schedule.task?.connection,
385
- headers: this.schedule.task?.connection?.headers || {}
386
- }
387
- }
388
- }
389
- }
390
-
391
- private async registerSchedule() {
392
- this.prepareSchedule()
393
-
394
- const { id, ...newSchedule } = this.schedule
395
-
396
- const response = await client.mutate({
397
- mutation: gql`
398
- mutation ($schedule: NewSchedule!) {
399
- handle: registerSchedule(schedule: $schedule)
400
- }
401
- `,
402
- variables: {
403
- schedule: newSchedule
404
- }
405
- })
406
-
407
- if (response.data?.handle) {
408
- this.scheduleId = response.data.handle
409
-
410
- notify({
411
- message: i18next.t('text.info_x_successfully', { x: i18next.t('text.register') })
412
- })
413
- }
414
- }
415
-
416
- private async updateSchedule() {
417
- this.prepareSchedule()
418
-
419
- const response = await client.mutate({
420
- mutation: gql`
421
- mutation UpdateSchedule($schedule: SchedulePatch!) {
422
- handle: updateSchedule(schedule: $schedule)
423
- }
424
- `,
425
- variables: {
426
- schedule: this.schedule
427
- }
428
- })
429
-
430
- if (response.data?.handle) {
431
- this.scheduleId = response.data.handle
432
-
433
- notify({
434
- message: i18next.t('text.info_x_successfully', { x: i18next.t('text.update') })
435
- })
436
- }
437
- }
438
-
439
- private async fetchSchedule(id: string) {
440
- const response = await client.query({
441
- query: gql`
442
- query ($id: ID!) {
443
- schedule(id: $id) {
444
- id
445
- name
446
- client {
447
- application
448
- group
449
- type
450
- key
451
- operation
452
- }
453
- type
454
- schedule
455
- timezone
456
- task {
457
- type
458
- connection {
459
- host
460
- headers
461
- topic
462
- }
463
- data
464
- history_check
465
- failed_policy
466
- retry_wait
467
- retry_count
468
- max_retry_count
469
- }
470
- }
471
- }
472
- `,
473
- variables: {
474
- id
475
- }
476
- })
477
-
478
- if (response.data?.schedule) {
479
- console.log(`a schedule fetched : ${response.data.schedule}`)
480
-
481
- return response.data.schedule
482
- }
483
- }
484
- }
package/client/route.ts DELETED
@@ -1,7 +0,0 @@
1
- export default function route(page: string) {
2
- switch (page) {
3
- case 'schedule-list':
4
- import('./pages/scheduler/schedule-list-page')
5
- return page
6
- }
7
- }
@@ -1,11 +0,0 @@
1
- {
2
- "extends": "../../tsconfig-base.json",
3
- "compilerOptions": {
4
- "strict": true,
5
- "declaration": true,
6
- "module": "esnext",
7
- "outDir": "../dist-client",
8
- "baseUrl": "./"
9
- },
10
- "include": ["./**/*"]
11
- }
package/client/types.ts DELETED
@@ -1,34 +0,0 @@
1
- export interface ScheduleClient {
2
- application?: string
3
- group?: string
4
- type?: string
5
- key?: string
6
- operation?: string
7
- }
8
-
9
- export interface ScheduleTaskConnection {
10
- host?: string
11
- headers?: Record<string, string>
12
- topic?: string
13
- }
14
-
15
- export interface ScheduleTask {
16
- type?: string
17
- connection?: ScheduleTaskConnection
18
- data?: { [key: string]: any }
19
- history_check?: boolean
20
- failed_policy?: 'ignore' | 'retry' | 'retry_dlq'
21
- retry_wait?: number
22
- retry_count?: number
23
- max_retry_count?: number
24
- }
25
-
26
- export interface Schedule {
27
- id?: string
28
- name?: string
29
- client?: ScheduleClient
30
- type?: 'now' | 'delay' | 'date' | 'cron' | 'delay-recur'
31
- schedule?: string
32
- timezone?: string
33
- task?: ScheduleTask
34
- }
File without changes
package/server/index.ts DELETED
@@ -1,3 +0,0 @@
1
- export * from './service'
2
-
3
- import './routes'
package/server/routes.ts DELETED
@@ -1,28 +0,0 @@
1
- const debug = require('debug')('things-factory:scheduler:routes')
2
-
3
- process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRouter) => {
4
- /*
5
- * can add global public routes to application (auth not required, tenancy not required)
6
- *
7
- * ex) routes.get('/path', async(context, next) => {})
8
- * ex) routes.post('/path', async(context, next) => {})
9
- */
10
- })
11
-
12
- process.on('bootstrap-module-global-private-route' as any, (app, globalPrivateRouter) => {
13
- /*
14
- * can add global private routes to application (auth required, tenancy not required)
15
- */
16
- })
17
-
18
- process.on('bootstrap-module-domain-public-route' as any, (app, domainPublicRouter) => {
19
- /*
20
- * can add domain public routes to application (auth not required, tenancy required)
21
- */
22
- })
23
-
24
- process.on('bootstrap-module-domain-private-route' as any, (app, domainPrivateRouter) => {
25
- /*
26
- * can add domain private routes to application (auth required, tenancy required)
27
- */
28
- })
@@ -1,25 +0,0 @@
1
- /* EXPORT ENTITY TYPES */
2
-
3
- /* IMPORT ENTITIES AND RESOLVERS */
4
- import {
5
- entities as SchedulerEntities,
6
- resolvers as SchedulerResolvers,
7
- subscribers as SchedulerSubscribers
8
- } from './schedule'
9
-
10
- export const entities = [
11
- /* ENTITIES */
12
- ...SchedulerEntities
13
- ]
14
-
15
- export const subscribers = [
16
- /* SUBSCRIBERS */
17
- ...SchedulerSubscribers
18
- ]
19
-
20
- export const schema = {
21
- resolverClasses: [
22
- /* RESOLVER CLASSES */
23
- ...SchedulerResolvers
24
- ]
25
- }
@@ -1,7 +0,0 @@
1
- import { Schedule } from './schedule-type'
2
- import { ScheduleQuery } from './schedule-query'
3
- import { ScheduleMutation } from './schedule-mutation'
4
-
5
- export const entities = [Schedule]
6
- export const resolvers = [ScheduleQuery, ScheduleMutation]
7
- export const subscribers = []
@@ -1,34 +0,0 @@
1
- import { Resolver, Directive, Mutation, Arg, ID } from 'type-graphql'
2
-
3
- import { registerSchedule, updateSchedule, unregisterSchedule } from '@things-factory/scheduler-client'
4
- import { Schedule, NewSchedule, SchedulePatch } from './schedule-type'
5
-
6
- @Resolver(Schedule)
7
- export class ScheduleMutation {
8
- @Directive('@privilege(superUserGranted: true)')
9
- @Mutation(() => ID)
10
- async registerSchedule(@Arg('schedule') schedule: NewSchedule): Promise<string> {
11
- var handle = await registerSchedule(schedule)
12
-
13
- return handle
14
- }
15
-
16
- @Directive('@privilege(superUserGranted: true)')
17
- @Mutation(() => ID)
18
- async updateSchedule(@Arg('schedule') schedule: SchedulePatch): Promise<string> {
19
- return await updateSchedule(schedule)
20
- }
21
-
22
- @Directive('@privilege(superUserGranted: true)')
23
- @Mutation(() => Boolean)
24
- async unregisterSchedule(@Arg('handle', () => ID) handle: string): Promise<boolean> {
25
- try {
26
- await unregisterSchedule(handle)
27
- } catch (error) {
28
- console.error(error)
29
- return false
30
- }
31
-
32
- return true
33
- }
34
- }
@@ -1,64 +0,0 @@
1
- import { Resolver, Query, Args, Arg, Ctx, Directive, ID } from 'type-graphql'
2
- import { ListParam } from '@things-factory/shell'
3
- import { User } from '@things-factory/auth-base'
4
- import { getSchedules, getSchedule } from '@things-factory/scheduler-client'
5
- import { Schedule, ScheduleList } from './schedule-type'
6
-
7
- @Resolver(Schedule)
8
- export class ScheduleQuery {
9
- @Directive('@privilege(superUserGranted: true)')
10
- @Query(() => Schedule, { nullable: true })
11
- async schedule(@Arg('id', () => ID) id: string): Promise<Schedule | null> {
12
- return await getSchedule(id)
13
- }
14
-
15
- @Directive('@privilege(superUserGranted: true)')
16
- @Query(returns => ScheduleList, { description: 'To fetch multiple Schedules' })
17
- async schedules(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<ScheduleList> {
18
- const filters = params.filters || []
19
-
20
- const name = filters.find(filter => filter.name == 'name')?.value
21
-
22
- const application = filters.find(filter => filter.name == 'application')?.value
23
- const group = filters.find(filter => filter.name == 'group')?.value
24
- const type = filters.find(filter => filter.name == 'type')?.value
25
- const key = filters.find(filter => filter.name == 'key')?.value
26
- const operation = filters.find(filter => filter.name == 'operation')?.value
27
-
28
- var schedules = await getSchedules({})
29
-
30
- if (name) {
31
- schedules = schedules.filter(schedule => (schedule.name || '').toLowerCase().includes(name.toLowerCase()))
32
- }
33
- if (application) {
34
- schedules = schedules.filter(schedule =>
35
- (schedule.client?.application || '').toLowerCase().includes(application.toLowerCase())
36
- )
37
- }
38
- if (group) {
39
- schedules = schedules.filter(schedule =>
40
- (schedule.client?.group || '').toLowerCase().includes(group.toLowerCase())
41
- )
42
- }
43
- if (type) {
44
- schedules = schedules.filter(schedule => (schedule.client?.type || '').toLowerCase().includes(type.toLowerCase()))
45
- }
46
- if (key) {
47
- schedules = schedules.filter(schedule => (schedule.client?.key || '').toLowerCase().includes(key.toLowerCase()))
48
- }
49
- if (operation) {
50
- schedules = schedules.filter(schedule =>
51
- (schedule.client?.operation || '').toLowerCase().includes(operation.toLowerCase())
52
- )
53
- }
54
-
55
- const { page = 1, limit = 20 } = params.pagination || {}
56
-
57
- const start = (page - 1) * limit
58
- const end = start + limit
59
- const items = schedules.slice(start, end)
60
- const total = schedules.length
61
-
62
- return { items, total }
63
- }
64
- }
@@ -1,194 +0,0 @@
1
- import { ObjectType, Field, InputType, Int, ID } from 'type-graphql'
2
- import { ScalarObject } from '@things-factory/shell'
3
-
4
- @ObjectType()
5
- export class ScheduleClient {
6
- @Field()
7
- application: string
8
-
9
- @Field()
10
- group: string
11
-
12
- @Field()
13
- type: string
14
-
15
- @Field()
16
- key: string
17
-
18
- @Field()
19
- operation: string
20
- }
21
-
22
- @ObjectType()
23
- export class ScheduleTaskConnection {
24
- @Field({ nullable: true })
25
- host?: string
26
-
27
- @Field(type => ScalarObject, { nullable: true })
28
- headers?: Record<string, string>
29
-
30
- @Field({ nullable: true })
31
- topic?: string
32
- }
33
-
34
- @ObjectType()
35
- export class ScheduleTask {
36
- @Field()
37
- type: string
38
-
39
- @Field(type => ScheduleTaskConnection)
40
- connection: ScheduleTaskConnection
41
-
42
- @Field(type => ScalarObject)
43
- data: Record<string, any>
44
-
45
- @Field()
46
- history_check: boolean
47
-
48
- @Field()
49
- failed_policy: 'ignore' | 'retry' | 'retry_dlq'
50
-
51
- @Field(type => Int)
52
- retry_wait: number
53
-
54
- @Field(type => Int)
55
- retry_count: number
56
-
57
- @Field(type => Int)
58
- max_retry_count: number
59
- }
60
-
61
- @ObjectType()
62
- export class Schedule {
63
- @Field(type => ID)
64
- id: string
65
-
66
- @Field({ nullable: true })
67
- name?: string
68
-
69
- @Field(type => ScheduleClient)
70
- client: ScheduleClient
71
-
72
- @Field()
73
- type: 'now' | 'delay' | 'date' | 'cron' | 'delay-recur'
74
-
75
- @Field({ nullable: true })
76
- schedule?: string
77
-
78
- @Field({ nullable: true })
79
- timezone?: string
80
-
81
- @Field(type => ScheduleTask)
82
- task: ScheduleTask
83
- }
84
-
85
- @InputType()
86
- class ScheduleClientInput {
87
- @Field()
88
- application: string
89
-
90
- @Field()
91
- group: string
92
-
93
- @Field()
94
- type: string
95
-
96
- @Field()
97
- key: string
98
-
99
- @Field()
100
- operation: string
101
- }
102
-
103
- @InputType()
104
- export class ScheduleTaskConnectionInput {
105
- @Field({ nullable: true })
106
- host?: string
107
-
108
- @Field(type => ScalarObject, { nullable: true })
109
- headers?: Record<string, string>
110
-
111
- @Field({ nullable: true })
112
- topic?: string
113
- }
114
-
115
- @InputType()
116
- class ScheduleTaskInput {
117
- @Field()
118
- type: 'rest' | 'kafka' | 'redis'
119
-
120
- @Field(type => ScheduleTaskConnectionInput)
121
- connection: ScheduleTaskConnectionInput
122
-
123
- @Field(type => ScalarObject)
124
- data: Record<string, any>
125
-
126
- @Field()
127
- history_check: boolean
128
-
129
- @Field()
130
- failed_policy: 'ignore' | 'retry' | 'retry_dlq'
131
-
132
- @Field(type => Int)
133
- retry_wait: number
134
-
135
- @Field(type => Int)
136
- retry_count: number
137
-
138
- @Field(type => Int)
139
- max_retry_count: number
140
- }
141
-
142
- @InputType()
143
- export class NewSchedule {
144
- @Field()
145
- name: string
146
-
147
- @Field(type => ScheduleClientInput)
148
- client: ScheduleClientInput
149
-
150
- @Field()
151
- type: 'now' | 'delay' | 'date' | 'cron' | 'delay-recur'
152
-
153
- @Field({ nullable: true })
154
- schedule: string
155
-
156
- @Field({ nullable: true })
157
- timezone: string
158
-
159
- @Field(type => ScheduleTaskInput)
160
- task: ScheduleTaskInput
161
- }
162
-
163
- @InputType()
164
- export class SchedulePatch {
165
- @Field({ nullable: true })
166
- id: string
167
-
168
- @Field({ nullable: true })
169
- name?: string
170
-
171
- @Field(type => ScheduleClientInput)
172
- client: ScheduleClientInput
173
-
174
- @Field()
175
- type: 'now' | 'delay' | 'date' | 'cron' | 'delay-recur'
176
-
177
- @Field({ nullable: true })
178
- schedule: string
179
-
180
- @Field({ nullable: true })
181
- timezone: string
182
-
183
- @Field(type => ScheduleTaskInput)
184
- task: ScheduleTaskInput
185
- }
186
-
187
- @ObjectType()
188
- export class ScheduleList {
189
- @Field(type => [Schedule])
190
- items: Schedule[]
191
-
192
- @Field(type => Int)
193
- total: number
194
- }
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "../../tsconfig-base.json",
3
- "compilerOptions": {
4
- "strict": false,
5
- "module": "commonjs",
6
- "outDir": "../dist-server",
7
- "baseUrl": "./"
8
- },
9
- "include": ["./**/*"]
10
- }