@gadmin2n/schematics 0.0.140 → 0.0.142
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/lib/application/files/gadmin2-game-angle-demo/config/ui/Event.ts +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/gen-jwt.ts +8 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +32 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow.seed.ts +39 -33
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.spec.ts +34 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.ts +26 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.spec.ts +149 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.ts +20 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-http-response.spec.ts +85 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-http-response.ts +157 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.spec.ts +59 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +15 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +125 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +109 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/PageHeader.tsx +46 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/index.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.test.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +6 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/constants/layout.ts +30 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/workflow.json +20 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/workflow.json +20 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/index.tsx +9 -20
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/list.tsx +141 -138
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasListPage.tsx +8 -19
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/human-approval/inbox.tsx +12 -19
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/list.tsx +47 -150
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/permissionReadme/index.tsx +13 -12
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/list.tsx +110 -113
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/list.tsx +61 -64
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/list.tsx +187 -200
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/index.tsx +46 -44
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NewNodeDefaults.test.tsx +6 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/index.tsx +50 -41
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/index.tsx +9 -22
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instances.tsx +4 -8
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/index.tsx +9 -22
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ExternalRequestConfigEditor.test.tsx +42 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ExternalRequestConfigEditor.tsx +77 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ReturnResultConfigEditor.test.tsx +29 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ReturnResultConfigEditor.tsx +245 -55
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.test.tsx +152 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.tsx +417 -161
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/newNodeConfig.ts +5 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/index.css +20 -0
- package/package.json +1 -1
|
@@ -17,6 +17,12 @@ import {
|
|
|
17
17
|
message,
|
|
18
18
|
} from 'antd';
|
|
19
19
|
import { customRequest } from 'helpers/http';
|
|
20
|
+
import { PageHeader } from 'components';
|
|
21
|
+
import {
|
|
22
|
+
NESTED_CARD_PROPS,
|
|
23
|
+
PAGE_WRAPPER_STYLE,
|
|
24
|
+
SEARCH_CARD_PROPS,
|
|
25
|
+
} from 'constants/layout';
|
|
20
26
|
import { useCallback, useEffect, useState } from 'react';
|
|
21
27
|
import { useTranslation } from 'react-i18next';
|
|
22
28
|
import { ApprovalChannelModal } from './ApprovalChannelModal';
|
|
@@ -48,50 +54,41 @@ export default function ApprovalChannelsPage() {
|
|
|
48
54
|
useEffect(() => void load(), [load]);
|
|
49
55
|
|
|
50
56
|
return (
|
|
51
|
-
<div style={
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
</Typography.Text>
|
|
69
|
-
</div>
|
|
70
|
-
<Button
|
|
71
|
-
type="primary"
|
|
72
|
-
icon={<PlusOutlined />}
|
|
73
|
-
onClick={() => setEditing(null)}
|
|
74
|
-
>
|
|
75
|
-
{t('approvalChannel.newChannel')}
|
|
76
|
-
</Button>
|
|
77
|
-
</div>
|
|
57
|
+
<div style={PAGE_WRAPPER_STYLE}>
|
|
58
|
+
<PageHeader
|
|
59
|
+
title={t('approvalChannel.title')}
|
|
60
|
+
subtitle={t('approvalChannel.subtitle')}
|
|
61
|
+
extra={
|
|
62
|
+
<Space>
|
|
63
|
+
<Button icon={<ReloadOutlined />} onClick={load} />
|
|
64
|
+
<Button
|
|
65
|
+
type="primary"
|
|
66
|
+
icon={<PlusOutlined />}
|
|
67
|
+
onClick={() => setEditing(null)}
|
|
68
|
+
>
|
|
69
|
+
{t('approvalChannel.newChannel')}
|
|
70
|
+
</Button>
|
|
71
|
+
</Space>
|
|
72
|
+
}
|
|
73
|
+
/>
|
|
78
74
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
</Button>
|
|
89
|
-
</Space>
|
|
75
|
+
{/* Search Card */}
|
|
76
|
+
<Card {...SEARCH_CARD_PROPS}>
|
|
77
|
+
<Input.Search
|
|
78
|
+
allowClear
|
|
79
|
+
placeholder={t('approvalChannel.search')}
|
|
80
|
+
onSearch={setQuery}
|
|
81
|
+
style={{ width: 300 }}
|
|
82
|
+
/>
|
|
83
|
+
</Card>
|
|
90
84
|
|
|
85
|
+
{/* Content Card */}
|
|
86
|
+
<Card>
|
|
91
87
|
{loading ? (
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
88
|
+
<Spin
|
|
89
|
+
size="large"
|
|
90
|
+
style={{ display: 'block', margin: '48px auto' }}
|
|
91
|
+
/>
|
|
95
92
|
) : channels.length === 0 ? (
|
|
96
93
|
<Empty description={t('approvalChannel.empty')} />
|
|
97
94
|
) : (
|
|
@@ -103,7 +100,12 @@ export default function ApprovalChannelsPage() {
|
|
|
103
100
|
}}
|
|
104
101
|
>
|
|
105
102
|
{channels.map((channel) => (
|
|
106
|
-
<Card
|
|
103
|
+
<Card
|
|
104
|
+
key={channel.code}
|
|
105
|
+
size="small"
|
|
106
|
+
hoverable
|
|
107
|
+
{...NESTED_CARD_PROPS}
|
|
108
|
+
>
|
|
107
109
|
<div
|
|
108
110
|
style={{
|
|
109
111
|
display: 'flex',
|
|
@@ -189,9 +191,9 @@ function WorkflowStep({
|
|
|
189
191
|
style={{
|
|
190
192
|
minWidth: 0,
|
|
191
193
|
padding: '10px 12px',
|
|
192
|
-
border: '1px solid
|
|
194
|
+
border: '1px solid var(--ant-color-border-secondary)',
|
|
193
195
|
borderRadius: 8,
|
|
194
|
-
background: '
|
|
196
|
+
background: 'var(--ant-color-fill-quaternary)',
|
|
195
197
|
}}
|
|
196
198
|
>
|
|
197
199
|
<Space size={6}>
|
|
@@ -117,7 +117,7 @@ describe.each([
|
|
|
117
117
|
['FlowRenderer', FlowRenderer],
|
|
118
118
|
['EnhancedFlowRenderer', EnhancedFlowRenderer],
|
|
119
119
|
] as const)('%s new-node defaults', (_name, Renderer) => {
|
|
120
|
-
it('gives a new Return Result node
|
|
120
|
+
it('gives a new Return Result node ordinary data from the previous step', () => {
|
|
121
121
|
const onDslChange = vi.fn();
|
|
122
122
|
render(
|
|
123
123
|
<Renderer dsl={{ nodes: [], edges: [] }} onDslChange={onDslChange} />,
|
|
@@ -131,8 +131,10 @@ describe.each([
|
|
|
131
131
|
expect.objectContaining({
|
|
132
132
|
type: 'return_result',
|
|
133
133
|
config: expect.objectContaining({
|
|
134
|
-
resultType: '
|
|
134
|
+
resultType: 'JSON',
|
|
135
135
|
sourceMode: 'INPUT',
|
|
136
|
+
httpStatusCode: 200,
|
|
137
|
+
httpHeaders: {},
|
|
136
138
|
}),
|
|
137
139
|
}),
|
|
138
140
|
],
|
|
@@ -187,6 +189,8 @@ describe.each([
|
|
|
187
189
|
path: expect.stringMatching(/^external-/),
|
|
188
190
|
secret: expect.stringMatching(/^[a-f0-9]{64}$/),
|
|
189
191
|
verificationMode: 'HMAC',
|
|
192
|
+
responseMode: 'IMMEDIATE',
|
|
193
|
+
responseTimeoutSeconds: 60,
|
|
190
194
|
}),
|
|
191
195
|
}),
|
|
192
196
|
],
|
|
@@ -18,6 +18,12 @@ import {
|
|
|
18
18
|
ReloadOutlined,
|
|
19
19
|
} from '@ant-design/icons';
|
|
20
20
|
import { customRequest } from 'helpers/http';
|
|
21
|
+
import { PageHeader } from 'components';
|
|
22
|
+
import {
|
|
23
|
+
NESTED_CARD_PROPS,
|
|
24
|
+
PAGE_WRAPPER_STYLE,
|
|
25
|
+
SEARCH_CARD_PROPS,
|
|
26
|
+
} from 'constants/layout';
|
|
21
27
|
import { ConnectionModal } from './ConnectionModal';
|
|
22
28
|
import type { IntegrationConnection } from './connection.types';
|
|
23
29
|
import { useTranslation } from 'react-i18next';
|
|
@@ -50,47 +56,41 @@ export default function ConnectionsPage() {
|
|
|
50
56
|
useEffect(() => void load(), [load]);
|
|
51
57
|
|
|
52
58
|
return (
|
|
53
|
-
<div style={
|
|
59
|
+
<div style={PAGE_WRAPPER_STYLE}>
|
|
60
|
+
<PageHeader
|
|
61
|
+
title={t('connection.title')}
|
|
62
|
+
subtitle={t('connection.subtitle')}
|
|
63
|
+
extra={
|
|
64
|
+
<Space>
|
|
65
|
+
<Button icon={<ReloadOutlined />} onClick={load} />
|
|
66
|
+
<Button
|
|
67
|
+
type="primary"
|
|
68
|
+
icon={<PlusOutlined />}
|
|
69
|
+
onClick={() => setEditing(null)}
|
|
70
|
+
>
|
|
71
|
+
{t('connection.new')}
|
|
72
|
+
</Button>
|
|
73
|
+
</Space>
|
|
74
|
+
}
|
|
75
|
+
/>
|
|
76
|
+
|
|
77
|
+
{/* Search Card */}
|
|
78
|
+
<Card {...SEARCH_CARD_PROPS}>
|
|
79
|
+
<Input.Search
|
|
80
|
+
allowClear
|
|
81
|
+
placeholder={t('connection.search')}
|
|
82
|
+
onSearch={setQuery}
|
|
83
|
+
style={{ width: 300 }}
|
|
84
|
+
/>
|
|
85
|
+
</Card>
|
|
86
|
+
|
|
87
|
+
{/* Content Card */}
|
|
54
88
|
<Card>
|
|
55
|
-
<div
|
|
56
|
-
style={{
|
|
57
|
-
display: 'flex',
|
|
58
|
-
justifyContent: 'space-between',
|
|
59
|
-
gap: 16,
|
|
60
|
-
marginBottom: 20,
|
|
61
|
-
}}
|
|
62
|
-
>
|
|
63
|
-
<div>
|
|
64
|
-
<Typography.Title level={3} style={{ margin: 0 }}>
|
|
65
|
-
{t('connection.title')}
|
|
66
|
-
</Typography.Title>
|
|
67
|
-
<Typography.Text type="secondary">
|
|
68
|
-
{t('connection.subtitle')}
|
|
69
|
-
</Typography.Text>
|
|
70
|
-
</div>
|
|
71
|
-
<Button
|
|
72
|
-
type="primary"
|
|
73
|
-
icon={<PlusOutlined />}
|
|
74
|
-
onClick={() => setEditing(null)}
|
|
75
|
-
>
|
|
76
|
-
{t('connection.new')}
|
|
77
|
-
</Button>
|
|
78
|
-
</div>
|
|
79
|
-
<Space style={{ marginBottom: 18 }}>
|
|
80
|
-
<Input.Search
|
|
81
|
-
allowClear
|
|
82
|
-
placeholder={t('connection.search')}
|
|
83
|
-
onSearch={setQuery}
|
|
84
|
-
style={{ width: 300 }}
|
|
85
|
-
/>
|
|
86
|
-
<Button icon={<ReloadOutlined />} onClick={load}>
|
|
87
|
-
{t('common.refresh')}
|
|
88
|
-
</Button>
|
|
89
|
-
</Space>
|
|
90
89
|
{loading ? (
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
<Spin
|
|
91
|
+
size="large"
|
|
92
|
+
style={{ display: 'block', margin: '48px auto' }}
|
|
93
|
+
/>
|
|
94
94
|
) : connections.length === 0 ? (
|
|
95
95
|
<Empty description={t('connection.empty')} />
|
|
96
96
|
) : (
|
|
@@ -102,7 +102,12 @@ export default function ConnectionsPage() {
|
|
|
102
102
|
}}
|
|
103
103
|
>
|
|
104
104
|
{connections.map((connection) => (
|
|
105
|
-
<Card
|
|
105
|
+
<Card
|
|
106
|
+
key={connection.code}
|
|
107
|
+
size="small"
|
|
108
|
+
hoverable
|
|
109
|
+
{...NESTED_CARD_PROPS}
|
|
110
|
+
>
|
|
106
111
|
<div
|
|
107
112
|
style={{
|
|
108
113
|
display: 'flex',
|
|
@@ -112,7 +117,11 @@ export default function ConnectionsPage() {
|
|
|
112
117
|
>
|
|
113
118
|
<Space align="start">
|
|
114
119
|
<ApiOutlined
|
|
115
|
-
style={{
|
|
120
|
+
style={{
|
|
121
|
+
color: 'var(--ant-color-primary)',
|
|
122
|
+
fontSize: 20,
|
|
123
|
+
marginTop: 3,
|
|
124
|
+
}}
|
|
116
125
|
/>
|
|
117
126
|
<div>
|
|
118
127
|
<Typography.Text strong>
|
package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/index.tsx
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
Table,
|
|
12
12
|
Tag,
|
|
13
13
|
Tooltip,
|
|
14
|
-
Typography,
|
|
15
14
|
message,
|
|
16
15
|
} from 'antd';
|
|
17
16
|
import {
|
|
@@ -33,8 +32,9 @@ import { ExportModal } from './components/ExportModal';
|
|
|
33
32
|
import { ImportModal } from './components/ImportModal';
|
|
34
33
|
import type { Workflow, WorkflowOverview } from './types';
|
|
35
34
|
import { useTranslation } from 'react-i18next';
|
|
35
|
+
import { PageHeader } from 'components';
|
|
36
|
+
import { PAGE_WRAPPER_STYLE, SEARCH_CARD_PROPS } from 'constants/layout';
|
|
36
37
|
|
|
37
|
-
const { Title } = Typography;
|
|
38
38
|
const PAGE_SIZE = 20;
|
|
39
39
|
|
|
40
40
|
function RunDot({
|
|
@@ -402,23 +402,10 @@ export default function WorkflowListPage() {
|
|
|
402
402
|
];
|
|
403
403
|
|
|
404
404
|
return (
|
|
405
|
-
<div
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}}
|
|
410
|
-
>
|
|
411
|
-
<Card style={{ marginBottom: 16 }}>
|
|
412
|
-
<div
|
|
413
|
-
style={{
|
|
414
|
-
display: 'flex',
|
|
415
|
-
justifyContent: 'space-between',
|
|
416
|
-
alignItems: 'center',
|
|
417
|
-
}}
|
|
418
|
-
>
|
|
419
|
-
<Title level={4} style={{ margin: 0 }}>
|
|
420
|
-
{t('list.title')}
|
|
421
|
-
</Title>
|
|
405
|
+
<div style={PAGE_WRAPPER_STYLE}>
|
|
406
|
+
<PageHeader
|
|
407
|
+
title={t('list.title')}
|
|
408
|
+
extra={
|
|
422
409
|
<Space>
|
|
423
410
|
<Tooltip title="查看 WorkflowEventOutbox 表原始数据">
|
|
424
411
|
<Button
|
|
@@ -445,8 +432,8 @@ export default function WorkflowListPage() {
|
|
|
445
432
|
</Button>
|
|
446
433
|
<Button icon={<ReloadOutlined />} onClick={fetchData} />
|
|
447
434
|
</Space>
|
|
448
|
-
|
|
449
|
-
|
|
435
|
+
}
|
|
436
|
+
/>
|
|
450
437
|
|
|
451
438
|
<StatusCards
|
|
452
439
|
overview={overview}
|
|
@@ -454,7 +441,7 @@ export default function WorkflowListPage() {
|
|
|
454
441
|
onFilter={setFilterInstanceStatus}
|
|
455
442
|
/>
|
|
456
443
|
|
|
457
|
-
<Card
|
|
444
|
+
<Card {...SEARCH_CARD_PROPS}>
|
|
458
445
|
<Space wrap>
|
|
459
446
|
<Select
|
|
460
447
|
placeholder="Status"
|
package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instances.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { ArrowLeftOutlined } from '@ant-design/icons';
|
|
13
13
|
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
|
14
14
|
import { customRequest } from 'helpers/http';
|
|
15
|
+
import { HEADER_CARD_PROPS, PAGE_WRAPPER_STYLE } from 'constants/layout';
|
|
15
16
|
|
|
16
17
|
const { Title } = Typography;
|
|
17
18
|
const PAGE_SIZE = 20;
|
|
@@ -183,13 +184,8 @@ export default function WorkflowInstancesPage() {
|
|
|
183
184
|
];
|
|
184
185
|
|
|
185
186
|
return (
|
|
186
|
-
<div
|
|
187
|
-
|
|
188
|
-
background: 'var(--ant-color-bg-layout)',
|
|
189
|
-
minHeight: 'calc(100vh - 64px)',
|
|
190
|
-
}}
|
|
191
|
-
>
|
|
192
|
-
<Card style={{ marginBottom: 16 }}>
|
|
187
|
+
<div style={PAGE_WRAPPER_STYLE}>
|
|
188
|
+
<Card {...HEADER_CARD_PROPS}>
|
|
193
189
|
<div
|
|
194
190
|
style={{
|
|
195
191
|
display: 'flex',
|
|
@@ -224,7 +220,7 @@ export default function WorkflowInstancesPage() {
|
|
|
224
220
|
</div>
|
|
225
221
|
</Card>
|
|
226
222
|
|
|
227
|
-
<Card>
|
|
223
|
+
<Card className="card-table">
|
|
228
224
|
<Table
|
|
229
225
|
rowKey="id"
|
|
230
226
|
loading={loading}
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
Table,
|
|
11
11
|
Tag,
|
|
12
12
|
Tooltip,
|
|
13
|
-
Typography,
|
|
14
13
|
message,
|
|
15
14
|
} from 'antd';
|
|
16
15
|
import {
|
|
@@ -22,9 +21,10 @@ import {
|
|
|
22
21
|
} from '@ant-design/icons';
|
|
23
22
|
import { useSearchParams, useNavigate } from 'react-router-dom';
|
|
24
23
|
import { customRequest } from 'helpers/http';
|
|
24
|
+
import { PageHeader } from 'components';
|
|
25
|
+
import { PAGE_WRAPPER_STYLE, SEARCH_CARD_PROPS } from 'constants/layout';
|
|
25
26
|
import type { WorkflowNodeType } from '../types';
|
|
26
27
|
|
|
27
|
-
const { Title } = Typography;
|
|
28
28
|
const PAGE_SIZE = 20;
|
|
29
29
|
|
|
30
30
|
interface NodeInstance {
|
|
@@ -226,23 +226,10 @@ export default function NodeInstanceListPage() {
|
|
|
226
226
|
];
|
|
227
227
|
|
|
228
228
|
return (
|
|
229
|
-
<div
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}}
|
|
234
|
-
>
|
|
235
|
-
<Card style={{ marginBottom: 16 }}>
|
|
236
|
-
<div
|
|
237
|
-
style={{
|
|
238
|
-
display: 'flex',
|
|
239
|
-
justifyContent: 'space-between',
|
|
240
|
-
alignItems: 'center',
|
|
241
|
-
}}
|
|
242
|
-
>
|
|
243
|
-
<Title level={4} style={{ margin: 0 }}>
|
|
244
|
-
Node instance mgnts
|
|
245
|
-
</Title>
|
|
229
|
+
<div style={PAGE_WRAPPER_STYLE}>
|
|
230
|
+
<PageHeader
|
|
231
|
+
title="Node instance mgnts"
|
|
232
|
+
extra={
|
|
246
233
|
<Space>
|
|
247
234
|
<Button
|
|
248
235
|
type="primary"
|
|
@@ -253,10 +240,10 @@ export default function NodeInstanceListPage() {
|
|
|
253
240
|
</Button>
|
|
254
241
|
<Button icon={<ReloadOutlined />} onClick={fetchData} />
|
|
255
242
|
</Space>
|
|
256
|
-
|
|
257
|
-
|
|
243
|
+
}
|
|
244
|
+
/>
|
|
258
245
|
|
|
259
|
-
<Card
|
|
246
|
+
<Card {...SEARCH_CARD_PROPS}>
|
|
260
247
|
<Space wrap>
|
|
261
248
|
<Select
|
|
262
249
|
placeholder="Node Type"
|
|
@@ -90,4 +90,46 @@ describe('ExternalRequestConfigEditor', () => {
|
|
|
90
90
|
screen.getByText(/Anyone who knows this endpoint/),
|
|
91
91
|
).toBeInTheDocument();
|
|
92
92
|
});
|
|
93
|
+
|
|
94
|
+
it('lets the caller wait for the final Workflow return value', async () => {
|
|
95
|
+
const onChange = vi.fn();
|
|
96
|
+
|
|
97
|
+
function Harness() {
|
|
98
|
+
const [value, setValue] = useState<
|
|
99
|
+
NonNullable<Parameters<typeof ExternalRequestConfigEditor>[0]['value']>
|
|
100
|
+
>({
|
|
101
|
+
path: 'create-ticket',
|
|
102
|
+
method: 'POST',
|
|
103
|
+
verificationMode: 'NONE',
|
|
104
|
+
responseMode: 'IMMEDIATE',
|
|
105
|
+
});
|
|
106
|
+
return (
|
|
107
|
+
<ExternalRequestConfigEditor
|
|
108
|
+
value={value}
|
|
109
|
+
onChange={(next) => {
|
|
110
|
+
onChange(next);
|
|
111
|
+
setValue(next);
|
|
112
|
+
}}
|
|
113
|
+
schema={schema}
|
|
114
|
+
/>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
render(<Harness />);
|
|
119
|
+
await screen.findByDisplayValue(
|
|
120
|
+
'http://10.20.30.40:14003/api/workflow/webhook/myoa-callback',
|
|
121
|
+
);
|
|
122
|
+
fireEvent.click(
|
|
123
|
+
screen.getByRole('radio', { name: /Wait for Workflow result/ }),
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
expect(onChange).toHaveBeenLastCalledWith({
|
|
127
|
+
path: 'create-ticket',
|
|
128
|
+
method: 'POST',
|
|
129
|
+
verificationMode: 'NONE',
|
|
130
|
+
responseMode: 'WAIT_FOR_RESULT',
|
|
131
|
+
responseTimeoutSeconds: 60,
|
|
132
|
+
});
|
|
133
|
+
expect(screen.getByRole('spinbutton')).toHaveValue('60');
|
|
134
|
+
});
|
|
93
135
|
});
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
Button,
|
|
5
5
|
Card,
|
|
6
6
|
Input,
|
|
7
|
+
InputNumber,
|
|
7
8
|
Radio,
|
|
8
9
|
Space,
|
|
9
10
|
Tag,
|
|
@@ -20,6 +21,8 @@ type ExternalRequestConfig = {
|
|
|
20
21
|
method?: string;
|
|
21
22
|
secret?: string;
|
|
22
23
|
verificationMode?: 'HMAC' | 'NONE';
|
|
24
|
+
responseMode?: 'IMMEDIATE' | 'WAIT_FOR_RESULT';
|
|
25
|
+
responseTimeoutSeconds?: number;
|
|
23
26
|
};
|
|
24
27
|
|
|
25
28
|
export function ExternalRequestConfigEditor({
|
|
@@ -30,6 +33,8 @@ export function ExternalRequestConfigEditor({
|
|
|
30
33
|
const { t } = useTranslation('workflow');
|
|
31
34
|
const config = value ?? {};
|
|
32
35
|
const verificationMode = config.verificationMode === 'NONE' ? 'NONE' : 'HMAC';
|
|
36
|
+
const responseMode =
|
|
37
|
+
config.responseMode === 'WAIT_FOR_RESULT' ? 'WAIT_FOR_RESULT' : 'IMMEDIATE';
|
|
33
38
|
const [endpoint, setEndpoint] = useState('');
|
|
34
39
|
const [endpointLoadFailed, setEndpointLoadFailed] = useState(false);
|
|
35
40
|
|
|
@@ -101,6 +106,78 @@ export function ExternalRequestConfigEditor({
|
|
|
101
106
|
</Space>
|
|
102
107
|
</Card>
|
|
103
108
|
|
|
109
|
+
<Card size="small" title={t('externalRequest.response')}>
|
|
110
|
+
<Radio.Group
|
|
111
|
+
value={responseMode}
|
|
112
|
+
disabled={disabled}
|
|
113
|
+
onChange={(event) => {
|
|
114
|
+
const nextMode = event.target.value as
|
|
115
|
+
| 'IMMEDIATE'
|
|
116
|
+
| 'WAIT_FOR_RESULT';
|
|
117
|
+
onChange({
|
|
118
|
+
...config,
|
|
119
|
+
method: 'POST',
|
|
120
|
+
responseMode: nextMode,
|
|
121
|
+
...(nextMode === 'WAIT_FOR_RESULT' && {
|
|
122
|
+
responseTimeoutSeconds:
|
|
123
|
+
config.responseTimeoutSeconds &&
|
|
124
|
+
config.responseTimeoutSeconds >= 1 &&
|
|
125
|
+
config.responseTimeoutSeconds <= 300
|
|
126
|
+
? config.responseTimeoutSeconds
|
|
127
|
+
: 60,
|
|
128
|
+
}),
|
|
129
|
+
});
|
|
130
|
+
}}
|
|
131
|
+
>
|
|
132
|
+
<Space direction="vertical" size={12}>
|
|
133
|
+
<Radio value="IMMEDIATE">
|
|
134
|
+
<Space direction="vertical" size={0}>
|
|
135
|
+
<Typography.Text>
|
|
136
|
+
{t('externalRequest.respondImmediately')}
|
|
137
|
+
</Typography.Text>
|
|
138
|
+
<Typography.Text type="secondary">
|
|
139
|
+
{t('externalRequest.respondImmediatelyHint')}
|
|
140
|
+
</Typography.Text>
|
|
141
|
+
</Space>
|
|
142
|
+
</Radio>
|
|
143
|
+
<Radio value="WAIT_FOR_RESULT">
|
|
144
|
+
<Space direction="vertical" size={0}>
|
|
145
|
+
<Typography.Text>
|
|
146
|
+
{t('externalRequest.waitForResult')}
|
|
147
|
+
</Typography.Text>
|
|
148
|
+
<Typography.Text type="secondary">
|
|
149
|
+
{t('externalRequest.waitForResultHint')}
|
|
150
|
+
</Typography.Text>
|
|
151
|
+
</Space>
|
|
152
|
+
</Radio>
|
|
153
|
+
</Space>
|
|
154
|
+
</Radio.Group>
|
|
155
|
+
{responseMode === 'WAIT_FOR_RESULT' && (
|
|
156
|
+
<div style={{ marginTop: 16 }}>
|
|
157
|
+
<Typography.Text>
|
|
158
|
+
{t('externalRequest.responseTimeout')}
|
|
159
|
+
</Typography.Text>
|
|
160
|
+
<InputNumber
|
|
161
|
+
min={1}
|
|
162
|
+
max={300}
|
|
163
|
+
precision={0}
|
|
164
|
+
value={config.responseTimeoutSeconds ?? 60}
|
|
165
|
+
disabled={disabled}
|
|
166
|
+
addonAfter={t('externalRequest.seconds')}
|
|
167
|
+
onChange={(next) =>
|
|
168
|
+
onChange({
|
|
169
|
+
...config,
|
|
170
|
+
method: 'POST',
|
|
171
|
+
responseMode: 'WAIT_FOR_RESULT',
|
|
172
|
+
responseTimeoutSeconds: next ?? 60,
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
style={{ display: 'block', marginTop: 6, width: '100%' }}
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
)}
|
|
179
|
+
</Card>
|
|
180
|
+
|
|
104
181
|
<Card size="small" title={t('externalRequest.verification')}>
|
|
105
182
|
<Radio.Group
|
|
106
183
|
value={verificationMode}
|
|
@@ -11,10 +11,11 @@ describe('ReturnResultConfigEditor', () => {
|
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
it('uses the connected previous node by default without asking for a path', () => {
|
|
14
|
+
const onChange = vi.fn();
|
|
14
15
|
render(
|
|
15
16
|
<ReturnResultConfigEditor
|
|
16
17
|
value={undefined}
|
|
17
|
-
onChange={
|
|
18
|
+
onChange={onChange}
|
|
18
19
|
schema={schema}
|
|
19
20
|
/>,
|
|
20
21
|
);
|
|
@@ -23,6 +24,12 @@ describe('ReturnResultConfigEditor', () => {
|
|
|
23
24
|
screen.getByText('Return the previous node result'),
|
|
24
25
|
).toBeInTheDocument();
|
|
25
26
|
expect(screen.queryByLabelText('Workflow data field')).toBeNull();
|
|
27
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
28
|
+
resultType: 'JSON',
|
|
29
|
+
sourceMode: 'INPUT',
|
|
30
|
+
httpStatusCode: 200,
|
|
31
|
+
httpHeaders: {},
|
|
32
|
+
});
|
|
26
33
|
});
|
|
27
34
|
|
|
28
35
|
it('can return a field from the original Workflow data', () => {
|
|
@@ -50,4 +57,25 @@ describe('ReturnResultConfigEditor', () => {
|
|
|
50
57
|
sourceMode: 'INPUT',
|
|
51
58
|
});
|
|
52
59
|
});
|
|
60
|
+
|
|
61
|
+
it('configures ordinary data with an HTTP status for external callers', () => {
|
|
62
|
+
const onChange = vi.fn();
|
|
63
|
+
render(
|
|
64
|
+
<ReturnResultConfigEditor
|
|
65
|
+
value={{
|
|
66
|
+
resultType: 'JSON',
|
|
67
|
+
sourceMode: 'INPUT',
|
|
68
|
+
httpStatusCode: 201,
|
|
69
|
+
httpHeaders: {},
|
|
70
|
+
}}
|
|
71
|
+
onChange={onChange}
|
|
72
|
+
schema={schema}
|
|
73
|
+
/>,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
expect(screen.getByRole('radio', { name: 'Ordinary data' })).toBeChecked();
|
|
77
|
+
expect(
|
|
78
|
+
screen.getByRole('spinbutton', { name: 'HTTP status code' }),
|
|
79
|
+
).toHaveValue('201');
|
|
80
|
+
});
|
|
53
81
|
});
|