Skip to content

Commit c7bd371

Browse files
committed
Supported import/export of arguments also for tasks without schema
1 parent 985bb56 commit c7bd371

File tree

7 files changed

+182
-136
lines changed

7 files changed

+182
-136
lines changed

src/lib/components/workflow/ArgumentForm.svelte

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import { updateFormEntry } from '$lib/components/workflow/task_form_utils';
44
import FormBuilder from '$lib/components/workflow/common/FormBuilder.svelte';
55
import { displayStandardErrorAlert } from '$lib/common/errors';
6+
import ImportExportArgs from './ImportExportArgs.svelte';
7+
import { onMount } from 'svelte';
68
79
// This component shall handle a form which the user can use to specify arguments of a workflow-task
810
// Upon interacting with this component, a representation of the arguments to be used with a workflow task
@@ -12,27 +14,30 @@
1214
// - store the sequence in a coherent object
1315
// - enable the usage of the object that keeps the representation of the list
1416
15-
export let workflowId = undefined;
16-
export let workflowTaskId = undefined;
17+
export let workflowId;
18+
/** @type {import('$lib/types').WorkflowTask} */
19+
export let workflowTask;
1720
18-
// The main property managed by this component
19-
export let workflowTaskArgs = {};
20-
21-
if (workflowTaskArgs == null || workflowTaskArgs === undefined) {
22-
workflowTaskArgs = {};
23-
}
21+
onMount(() => {
22+
if (!workflowTask.args) {
23+
workflowTask.args = {};
24+
}
25+
});
2426
27+
/**
28+
* @param {object} updatedEntry
29+
*/
2530
async function handleEntryUpdate(updatedEntry) {
2631
const projectId = $page.params.projectId;
2732
try {
2833
const response = await updateFormEntry(
2934
projectId,
3035
workflowId,
31-
workflowTaskId,
36+
workflowTask.id,
3237
updatedEntry,
3338
'args'
3439
);
35-
workflowTaskArgs = response.args;
40+
workflowTask.args = response.args;
3641
} catch (error) {
3742
console.error(error);
3843
displayStandardErrorAlert(error, 'argsPropertiesFormError');
@@ -42,5 +47,21 @@
4247

4348
<div>
4449
<span id="argsPropertiesFormError" />
45-
<FormBuilder entry={workflowTaskArgs} updateEntry={handleEntryUpdate} />
50+
<FormBuilder entry={workflowTask.args} updateEntry={handleEntryUpdate} />
51+
<div class="d-flex args-controls-bar p-3 mt-3">
52+
<ImportExportArgs
53+
taskName={workflowTask.task.name}
54+
args={workflowTask.args}
55+
onImport={(json) => handleEntryUpdate(json)}
56+
exportDisabled={false}
57+
/>
58+
</div>
4659
</div>
60+
61+
<style>
62+
.args-controls-bar {
63+
background-color: whitesmoke;
64+
margin-top: 5px;
65+
border-top: 1px solid lightgray;
66+
}
67+
</style>

src/lib/components/workflow/ArgumentsSchema.svelte

Lines changed: 9 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
import JSchema from '$lib/components/common/jschema/JSchema.svelte';
55
import { updateFormEntry } from '$lib/components/workflow/task_form_utils';
66
import { displayStandardErrorAlert } from '$lib/common/errors';
7-
import Modal from '../common/Modal.svelte';
8-
import { downloadBlob } from '$lib/common/component_utilities';
7+
import ImportExportArgs from './ImportExportArgs.svelte';
98
109
const SUPPORTED_SCHEMA_VERSIONS = ['pydantic_v1'];
1110
@@ -17,6 +16,8 @@
1716
export let argumentsSchemaVersion = undefined;
1817
export let validSchema = undefined;
1918
export let args = undefined;
19+
/** @type {string} */
20+
export let taskName;
2021
2122
/** @type {JSchema} */
2223
let schemaComponent;
@@ -58,47 +59,6 @@
5859
validSchema = false;
5960
}
6061
}
61-
62-
function exportArgs() {
63-
const serializedArgs = JSON.stringify(args, null, 2);
64-
downloadBlob(serializedArgs, 'args.json', 'text/json;charset=utf-8;');
65-
}
66-
67-
/** @type {Modal} */
68-
let importArgsModal;
69-
70-
/** @type {FileList|null} */
71-
let importArgsFiles = null;
72-
/** @type {HTMLInputElement|undefined} */
73-
let importArgsFileInput = undefined;
74-
let importArgsError = '';
75-
76-
function onImportArgsModalOpen() {
77-
importArgsFiles = null;
78-
if (importArgsFileInput) {
79-
importArgsFileInput.value = '';
80-
}
81-
importArgsError = '';
82-
}
83-
84-
async function importArgs() {
85-
importArgsError = '';
86-
if (importArgsFiles === null || importArgsFiles.length === 0) {
87-
return;
88-
}
89-
const file = importArgsFiles[0];
90-
let content = await file.text();
91-
let json;
92-
try {
93-
json = JSON.parse(content);
94-
} catch (err) {
95-
importArgsError = "File doesn't contain valid JSON";
96-
return;
97-
}
98-
importArgsModal.confirmAndHide(async function () {
99-
await schemaComponent.saveChanges(json);
100-
});
101-
}
10262
</script>
10363

10464
<div id="workflow-arguments-schema-panel">
@@ -116,21 +76,12 @@
11676
/>
11777
</div>
11878
<div class="d-flex jschema-controls-bar p-3">
119-
<div />
120-
<div class="me-auto">
121-
<button class="btn btn-outline-primary" on:click={() => importArgsModal.show()}>
122-
<i class="bi bi-upload" />
123-
Import
124-
</button>
125-
<button
126-
class="btn btn-outline-primary"
127-
on:click={exportArgs}
128-
disabled={unsavedChanges || savingChanges}
129-
>
130-
<i class="bi bi-download" />
131-
Export
132-
</button>
133-
</div>
79+
<ImportExportArgs
80+
{taskName}
81+
{args}
82+
onImport={(json) => schemaComponent.saveChanges(json)}
83+
exportDisabled={unsavedChanges || savingChanges}
84+
/>
13485
<div>
13586
<button
13687
class="btn btn-warning"
@@ -155,59 +106,6 @@
155106
</div>
156107
</div>
157108

158-
<Modal
159-
id="importArgumentsModal"
160-
onOpen={onImportArgsModalOpen}
161-
bind:this={importArgsModal}
162-
size="lg"
163-
>
164-
<svelte:fragment slot="header">
165-
<h1 class="h5 modal-title">Import arguments</h1>
166-
</svelte:fragment>
167-
<svelte:fragment slot="body">
168-
<div class="row">
169-
<div class="col needs-validation">
170-
<label for="importArgsFile"> Select arguments file </label>
171-
<input
172-
class="form-control mt-1"
173-
accept="application/json"
174-
type="file"
175-
name="importArgsFile"
176-
id="importArgsFile"
177-
bind:this={importArgsFileInput}
178-
bind:files={importArgsFiles}
179-
class:is-invalid={importArgsError}
180-
/>
181-
<span class="invalid-feedback">{importArgsError}</span>
182-
</div>
183-
<div class="form-text">JSON containing workflow task arguments</div>
184-
</div>
185-
<div class="row">
186-
<div class="col">
187-
<div class="alert alert-warning mt-3">
188-
<i class="bi bi-exclamation-triangle" />
189-
The current arguments will be completely overwritten, disregarding any pending changes.
190-
</div>
191-
</div>
192-
</div>
193-
<div class="row">
194-
<div class="col">
195-
<div id="errorAlert-importArgumentsModal" />
196-
</div>
197-
</div>
198-
</svelte:fragment>
199-
<svelte:fragment slot="footer">
200-
<button class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
201-
<button
202-
class="btn btn-primary"
203-
disabled={importArgsFiles === null || importArgsFiles.length === 0}
204-
on:click={importArgs}
205-
>
206-
Confirm
207-
</button>
208-
</svelte:fragment>
209-
</Modal>
210-
211109
<style>
212110
#workflow-arguments-schema-panel .args-list {
213111
overflow-y: auto;
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<script>
2+
import { downloadBlob } from '$lib/common/component_utilities';
3+
import Modal from '../common/Modal.svelte';
4+
5+
/** @type {string} */
6+
export let taskName;
7+
/** @type {object} */
8+
export let args;
9+
/** @type {(json: string) => Promise<void>} */
10+
export let onImport;
11+
/** @type {boolean} */
12+
export let exportDisabled;
13+
14+
/** @type {Modal} */
15+
let importArgsModal;
16+
17+
/** @type {FileList|null} */
18+
let importArgsFiles = null;
19+
/** @type {HTMLInputElement|undefined} */
20+
let importArgsFileInput = undefined;
21+
let importArgsError = '';
22+
23+
function onImportArgsModalOpen() {
24+
importArgsFiles = null;
25+
if (importArgsFileInput) {
26+
importArgsFileInput.value = '';
27+
}
28+
importArgsError = '';
29+
}
30+
31+
async function importArgs() {
32+
importArgsError = '';
33+
if (importArgsFiles === null || importArgsFiles.length === 0) {
34+
return;
35+
}
36+
const file = importArgsFiles[0];
37+
let content = await file.text();
38+
let json;
39+
try {
40+
json = JSON.parse(content);
41+
} catch (err) {
42+
importArgsError = "File doesn't contain valid JSON";
43+
return;
44+
}
45+
importArgsModal.confirmAndHide(async function () {
46+
await onImport(json);
47+
});
48+
}
49+
50+
function exportArgs() {
51+
const serializedArgs = JSON.stringify(args, null, 2);
52+
downloadBlob(serializedArgs, `args-${createSlug(taskName)}.json`, 'text/json;charset=utf-8;');
53+
}
54+
55+
/**
56+
* @param {string} value
57+
*/
58+
function createSlug(value) {
59+
return value.trim().toLowerCase().replace(/\s+/g, '-');
60+
}
61+
</script>
62+
63+
<div class="me-auto">
64+
<button class="btn btn-outline-primary" on:click={() => importArgsModal.show()}>
65+
<i class="bi bi-upload" />
66+
Import
67+
</button>
68+
<button class="btn btn-outline-primary" on:click={exportArgs} disabled={exportDisabled}>
69+
<i class="bi bi-download" />
70+
Export
71+
</button>
72+
</div>
73+
74+
<Modal
75+
id="importArgumentsModal"
76+
onOpen={onImportArgsModalOpen}
77+
bind:this={importArgsModal}
78+
size="lg"
79+
>
80+
<svelte:fragment slot="header">
81+
<h1 class="h5 modal-title">Import arguments</h1>
82+
</svelte:fragment>
83+
<svelte:fragment slot="body">
84+
<div class="row">
85+
<div class="col needs-validation">
86+
<label for="importArgsFile"> Select arguments file </label>
87+
<input
88+
class="form-control mt-1"
89+
accept="application/json"
90+
type="file"
91+
name="importArgsFile"
92+
id="importArgsFile"
93+
bind:this={importArgsFileInput}
94+
bind:files={importArgsFiles}
95+
class:is-invalid={importArgsError}
96+
/>
97+
<span class="invalid-feedback">{importArgsError}</span>
98+
</div>
99+
<div class="form-text">JSON containing workflow task arguments</div>
100+
</div>
101+
<div class="row">
102+
<div class="col">
103+
<div class="alert alert-warning mt-3">
104+
<i class="bi bi-exclamation-triangle" />
105+
The current arguments will be completely overwritten, disregarding any pending changes.
106+
</div>
107+
</div>
108+
</div>
109+
<div class="row">
110+
<div class="col">
111+
<div id="errorAlert-importArgumentsModal" />
112+
</div>
113+
</div>
114+
</svelte:fragment>
115+
<svelte:fragment slot="footer">
116+
<button class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
117+
<button
118+
class="btn btn-primary"
119+
disabled={importArgsFiles === null || importArgsFiles.length === 0}
120+
on:click={importArgs}
121+
>
122+
Confirm
123+
</button>
124+
</svelte:fragment>
125+
</Modal>

src/lib/components/workflow/common/NewEntryProperty.svelte

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,12 @@
122122
</form>
123123
</div>
124124
<div>
125-
<button class="btn btn-primary" type="submit" form={uuid}
126-
><i class="bi-check-square" /></button
127-
>
128-
<button class="btn btn-danger" on:click={resetComponent}><i class="bi-trash" /></button>
125+
<button class="btn btn-primary" type="submit" form={uuid} aria-label="Save argument">
126+
<i class="bi-check-square" />
127+
</button>
128+
<button class="btn btn-danger" on:click={resetComponent} aria-label="Delete argument">
129+
<i class="bi-trash" />
130+
</button>
129131
</div>
130132
</div>
131133
{/if}

0 commit comments

Comments
 (0)