nucleus-core-ts 0.9.825 → 0.9.826
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/fe/components/IntegrationsPage/components/MappingEditor.js +13 -0
- package/dist/fe/components/IntegrationsPage/components/MappingScheduleFields.d.ts +14 -0
- package/dist/fe/components/IntegrationsPage/components/MappingScheduleFields.js +150 -0
- package/dist/fe/components/IntegrationsPage/components/mappingDraft.d.ts +4 -0
- package/dist/fe/components/IntegrationsPage/components/mappingDraft.js +6 -0
- package/dist/fe/components/IntegrationsPage/index.d.ts +1 -0
- package/dist/fe/components/IntegrationsPage/index.js +1 -0
- package/dist/fe/components/IntegrationsPage/types/records.d.ts +4 -0
- package/dist/index.js +1 -1
- package/dist/src/ElysiaPlugin/routes/integrations/repository.d.ts +14 -0
- package/dist/src/Services/Integrations/cron.d.ts +59 -0
- package/dist/src/Services/Integrations/cron.test.d.ts +1 -0
- package/dist/src/Services/Integrations/scheduler.d.ts +66 -0
- package/dist/src/Services/Integrations/scheduler.test.d.ts +1 -0
- package/dist/src/types.d.ts +8 -0
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ import { FieldMappingRows } from './FieldMappingRows';
|
|
|
7
7
|
import { endpointLabel, fieldsOfStoredSample } from './formSupport';
|
|
8
8
|
import { MappingAccountFields } from './MappingAccountFields';
|
|
9
9
|
import { MappingPasswordField } from './MappingPasswordField';
|
|
10
|
+
import { MappingScheduleFields } from './MappingScheduleFields';
|
|
10
11
|
import { MappingWriteModeFields } from './MappingWriteModeFields';
|
|
11
12
|
import { mappingDraftIssues } from './mappingDraft';
|
|
12
13
|
import { Field } from './Primitives';
|
|
@@ -213,6 +214,18 @@ export function MappingEditor({ mapping, targets, targetsLoading, endpoints, fre
|
|
|
213
214
|
targets: targets,
|
|
214
215
|
value: draft.accountRule ?? null
|
|
215
216
|
}),
|
|
217
|
+
/*#__PURE__*/ _jsx(MappingScheduleFields, {
|
|
218
|
+
cron: draft.scheduleCron ?? '',
|
|
219
|
+
disabled: busy,
|
|
220
|
+
enabled: draft.scheduleEnabled ?? false,
|
|
221
|
+
needsPassword: Boolean(draft.accountRule || draft.passwordTargetColumn),
|
|
222
|
+
onChange: (next)=>patch({
|
|
223
|
+
scheduleEnabled: next.enabled,
|
|
224
|
+
scheduleCron: next.cron,
|
|
225
|
+
scheduleTimezone: next.timezone
|
|
226
|
+
}),
|
|
227
|
+
timezone: draft.scheduleTimezone ?? ''
|
|
228
|
+
}),
|
|
216
229
|
/*#__PURE__*/ _jsx(MappingPasswordField, {
|
|
217
230
|
disabled: busy,
|
|
218
231
|
onChange: (value)=>patch({
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type MappingScheduleFieldsProps = {
|
|
2
|
+
enabled: boolean;
|
|
3
|
+
cron: string;
|
|
4
|
+
timezone: string;
|
|
5
|
+
/** A mapping that creates logins cannot run unattended — there is no password. */
|
|
6
|
+
needsPassword?: boolean;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
onChange: (next: {
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
cron: string;
|
|
11
|
+
timezone: string;
|
|
12
|
+
}) => void;
|
|
13
|
+
};
|
|
14
|
+
export declare function MappingScheduleFields({ enabled, cron, timezone, needsPassword, disabled, onChange, }: MappingScheduleFieldsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from 'react';
|
|
4
|
+
import { describeCron, parseCron } from '../../../../src/Services/Integrations/cron';
|
|
5
|
+
import { integrationsPageTheme } from '../theme';
|
|
6
|
+
import { Field, Notice } from './Primitives';
|
|
7
|
+
/**
|
|
8
|
+
* Running this import without anybody clicking anything.
|
|
9
|
+
*
|
|
10
|
+
* The mapping has carried a cron, a timezone and an enabled flag since it was
|
|
11
|
+
* first designed, and nothing read them — an operator could fill one in, save
|
|
12
|
+
* it, see it on the screen, and it would never run. This is the screen for the
|
|
13
|
+
* half that now does.
|
|
14
|
+
*
|
|
15
|
+
* The expression is checked as it is typed and read back in plain words. Cron is
|
|
16
|
+
* a language people write once a year, and "0 2 * * *" and "2 0 * * *" look
|
|
17
|
+
* alike until one of them has been importing at two minutes past midnight for a
|
|
18
|
+
* month.
|
|
19
|
+
*/ const theme = integrationsPageTheme;
|
|
20
|
+
/** Offered because they are what people actually mean, spelled correctly. */ const PRESETS = [
|
|
21
|
+
{
|
|
22
|
+
label: 'Every night at 02:00',
|
|
23
|
+
cron: '0 2 * * *'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: 'Every weekday at 07:00',
|
|
27
|
+
cron: '0 7 * * 1-5'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
label: 'Every Monday at 06:00',
|
|
31
|
+
cron: '0 6 * * 1'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
label: 'Every hour',
|
|
35
|
+
cron: '0 * * * *'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
label: 'Every 15 minutes',
|
|
39
|
+
cron: '*/15 * * * *'
|
|
40
|
+
}
|
|
41
|
+
];
|
|
42
|
+
export function MappingScheduleFields({ enabled, cron, timezone, needsPassword, disabled, onChange }) {
|
|
43
|
+
const uid = useId();
|
|
44
|
+
const parsed = cron.trim() === '' ? null : parseCron(cron);
|
|
45
|
+
const invalid = parsed !== null && !parsed.ok;
|
|
46
|
+
const patch = (next)=>{
|
|
47
|
+
onChange({
|
|
48
|
+
enabled,
|
|
49
|
+
cron,
|
|
50
|
+
timezone,
|
|
51
|
+
...next
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
return /*#__PURE__*/ _jsxs("section", {
|
|
55
|
+
className: "flex flex-col gap-3",
|
|
56
|
+
children: [
|
|
57
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
58
|
+
className: "flex flex-wrap items-center justify-between gap-2",
|
|
59
|
+
children: [
|
|
60
|
+
/*#__PURE__*/ _jsx("h3", {
|
|
61
|
+
className: theme.card.title,
|
|
62
|
+
children: "Run this import on a schedule"
|
|
63
|
+
}),
|
|
64
|
+
/*#__PURE__*/ _jsxs("label", {
|
|
65
|
+
className: "flex items-center gap-2 text-sm",
|
|
66
|
+
children: [
|
|
67
|
+
/*#__PURE__*/ _jsx("input", {
|
|
68
|
+
checked: enabled,
|
|
69
|
+
disabled: disabled,
|
|
70
|
+
id: `${uid}-enabled`,
|
|
71
|
+
onChange: (event)=>patch({
|
|
72
|
+
enabled: event.target.checked
|
|
73
|
+
}),
|
|
74
|
+
type: "checkbox"
|
|
75
|
+
}),
|
|
76
|
+
/*#__PURE__*/ _jsx("span", {
|
|
77
|
+
className: theme.field.label,
|
|
78
|
+
children: "Enabled"
|
|
79
|
+
})
|
|
80
|
+
]
|
|
81
|
+
})
|
|
82
|
+
]
|
|
83
|
+
}),
|
|
84
|
+
enabled ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
85
|
+
children: [
|
|
86
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
87
|
+
className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
|
|
88
|
+
children: [
|
|
89
|
+
/*#__PURE__*/ _jsx(Field, {
|
|
90
|
+
error: invalid && !parsed.ok ? parsed.error : null,
|
|
91
|
+
hint: cron.trim() === '' ? 'Five parts: minute hour day-of-month month day-of-week.' : describeCron(cron, timezone),
|
|
92
|
+
htmlFor: `${uid}-cron`,
|
|
93
|
+
label: "When",
|
|
94
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
95
|
+
className: theme.field.input,
|
|
96
|
+
disabled: disabled,
|
|
97
|
+
id: `${uid}-cron`,
|
|
98
|
+
onChange: (event)=>patch({
|
|
99
|
+
cron: event.target.value
|
|
100
|
+
}),
|
|
101
|
+
placeholder: "0 2 * * *",
|
|
102
|
+
value: cron
|
|
103
|
+
})
|
|
104
|
+
}),
|
|
105
|
+
/*#__PURE__*/ _jsx(Field, {
|
|
106
|
+
hint: "The company's own clock, so 02:00 stays 02:00 across a daylight-saving change.",
|
|
107
|
+
htmlFor: `${uid}-timezone`,
|
|
108
|
+
label: "In this timezone",
|
|
109
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
110
|
+
className: theme.field.input,
|
|
111
|
+
disabled: disabled,
|
|
112
|
+
id: `${uid}-timezone`,
|
|
113
|
+
onChange: (event)=>patch({
|
|
114
|
+
timezone: event.target.value
|
|
115
|
+
}),
|
|
116
|
+
placeholder: "Europe/Istanbul",
|
|
117
|
+
value: timezone
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
]
|
|
121
|
+
}),
|
|
122
|
+
/*#__PURE__*/ _jsx("div", {
|
|
123
|
+
className: "flex flex-wrap gap-2",
|
|
124
|
+
children: PRESETS.map((preset)=>/*#__PURE__*/ _jsx("button", {
|
|
125
|
+
className: theme.button.ghost,
|
|
126
|
+
disabled: disabled,
|
|
127
|
+
onClick: ()=>patch({
|
|
128
|
+
cron: preset.cron
|
|
129
|
+
}),
|
|
130
|
+
type: "button",
|
|
131
|
+
children: preset.label
|
|
132
|
+
}, preset.cron))
|
|
133
|
+
}),
|
|
134
|
+
needsPassword ? /*#__PURE__*/ _jsx(Notice, {
|
|
135
|
+
title: "This import cannot run on its own",
|
|
136
|
+
tone: "warning",
|
|
137
|
+
children: "It creates a login for every new record, and a login needs a password — which there is nobody to type at that hour. Either start this import by hand, or turn the login off and give people their accounts another way."
|
|
138
|
+
}) : null,
|
|
139
|
+
/*#__PURE__*/ _jsx("p", {
|
|
140
|
+
className: theme.field.hint,
|
|
141
|
+
children: "Every server running this app checks the schedule, and exactly one of them takes the import — the same lock that stops two people running it at once."
|
|
142
|
+
})
|
|
143
|
+
]
|
|
144
|
+
}) : /*#__PURE__*/ _jsx("p", {
|
|
145
|
+
className: theme.list.empty,
|
|
146
|
+
children: "Off. This import runs when somebody asks for it, and at no other time."
|
|
147
|
+
})
|
|
148
|
+
]
|
|
149
|
+
});
|
|
150
|
+
}
|
|
@@ -31,6 +31,10 @@ export type MappingDraft = {
|
|
|
31
31
|
passwordTargetColumn?: string | null;
|
|
32
32
|
/** Creates a login for each new record, in the same import. */
|
|
33
33
|
accountRule?: AccountRuleValue | null;
|
|
34
|
+
/** Runs on its own, when the expression names this minute. */
|
|
35
|
+
scheduleEnabled?: boolean;
|
|
36
|
+
scheduleCron?: string | null;
|
|
37
|
+
scheduleTimezone?: string | null;
|
|
34
38
|
enabled?: boolean;
|
|
35
39
|
};
|
|
36
40
|
/**
|
|
@@ -32,6 +32,9 @@ import { missingRecordFieldsIncomplete } from './MappingWriteModeFields';
|
|
|
32
32
|
referenceChecks: asArray(mapping.referenceChecks),
|
|
33
33
|
passwordTargetColumn: mapping.passwordTargetColumn ?? null,
|
|
34
34
|
accountRule: mapping.accountRule ?? null,
|
|
35
|
+
scheduleEnabled: mapping.scheduleEnabled ?? false,
|
|
36
|
+
scheduleCron: mapping.scheduleCron ?? '',
|
|
37
|
+
scheduleTimezone: mapping.scheduleTimezone ?? '',
|
|
35
38
|
enabled: mapping.enabled
|
|
36
39
|
};
|
|
37
40
|
}
|
|
@@ -47,6 +50,9 @@ import { missingRecordFieldsIncomplete } from './MappingWriteModeFields';
|
|
|
47
50
|
if (draft.endpointId.trim() === '') issues.push('Select the endpoint this mapping reads from.');
|
|
48
51
|
const usable = asArray(draft.fieldMappings).some((row)=>row.target.trim() !== '' && (row.source.trim() !== '' || (row.constant ?? '').trim() !== ''));
|
|
49
52
|
if (!usable) issues.push('Map at least one field to a target column.');
|
|
53
|
+
if (draft.scheduleEnabled && (draft.scheduleCron ?? '').trim() === '') {
|
|
54
|
+
issues.push('A schedule that is on needs to say when.');
|
|
55
|
+
}
|
|
50
56
|
const account = draft.accountRule;
|
|
51
57
|
if (account) {
|
|
52
58
|
const missing = [
|
|
@@ -5,6 +5,7 @@ export { ConnectionTransfer } from './components/ConnectionTransfer';
|
|
|
5
5
|
export { EndpointsTab } from './components/EndpointsTab';
|
|
6
6
|
export { IntegrationsPage } from './components/IntegrationsPage';
|
|
7
7
|
export { MappingAccountFields } from './components/MappingAccountFields';
|
|
8
|
+
export { MappingScheduleFields } from './components/MappingScheduleFields';
|
|
8
9
|
export { MappingsTab } from './components/MappingsTab';
|
|
9
10
|
export type { BadgeProps, BadgeTone, CodeBlockProps, FieldProps, NoticeProps, NoticeTone, StatProps, } from './components/Primitives';
|
|
10
11
|
export { Badge, CodeBlock, Field, Notice, Stat } from './components/Primitives';
|
|
@@ -4,6 +4,7 @@ export { ConnectionTransfer } from './components/ConnectionTransfer';
|
|
|
4
4
|
export { EndpointsTab } from './components/EndpointsTab';
|
|
5
5
|
export { IntegrationsPage } from './components/IntegrationsPage';
|
|
6
6
|
export { MappingAccountFields } from './components/MappingAccountFields';
|
|
7
|
+
export { MappingScheduleFields } from './components/MappingScheduleFields';
|
|
7
8
|
export { MappingsTab } from './components/MappingsTab';
|
|
8
9
|
export { Badge, CodeBlock, Field, Notice, Stat } from './components/Primitives';
|
|
9
10
|
export { RunPlanSuggestions } from './components/RunPlanSuggestions';
|
|
@@ -152,6 +152,10 @@ export type IntegrationMapping = {
|
|
|
152
152
|
passwordTargetColumn?: string | null;
|
|
153
153
|
/** Creates a login for each new record, in the same import. */
|
|
154
154
|
accountRule?: AccountRuleValue | null;
|
|
155
|
+
/** Runs on its own, without anybody clicking anything. */
|
|
156
|
+
scheduleEnabled?: boolean | null;
|
|
157
|
+
scheduleCron?: string | null;
|
|
158
|
+
scheduleTimezone?: string | null;
|
|
155
159
|
lastRunAt?: string | null;
|
|
156
160
|
enabled?: boolean;
|
|
157
161
|
};
|