Skip to content

Commit

Permalink
Merge pull request #188 from fractal-analytics-platform/jschema_integ…
Browse files Browse the repository at this point in the history
…ration_with_workflow_page

Integrate JSchema component within Workflow page
  • Loading branch information
rkpasia authored Jun 21, 2023
2 parents 22a1edf + aba16b6 commit 45e7f19
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 49 deletions.
85 changes: 55 additions & 30 deletions src/lib/components/common/jschema/JSchema.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@
const validator = new SchemaValidator();
export let schema = undefined;
export let schemaData = undefined;
export let handleSaveChanges = undefined;
export let handleValidationErrors = undefined;
let validatedSchema = undefined;
let parsedSchema = undefined;
let isSchemaValid = undefined;
let data = undefined;
let isDataValid = undefined;
let schemaManager = undefined;
Expand All @@ -44,12 +45,9 @@
// Load a default schema
if (schema !== undefined) {
stripSchemaProperties(schema);
validatedSchema = schema;
parsedSchema = schema;
}
if (schemaData !== undefined) {
data = schemaData;
}
// Load schema and data from server
// Validate schema
if (isSchemaValid && isDataValid) {
Expand All @@ -62,17 +60,18 @@
$: {
if (schema !== undefined) {
stripSchemaProperties(schema);
validatedSchema = schema;
isSchemaValid = validator.loadSchema(validatedSchema);
console.log('Validator loaded schema', validator.getErrors());
parsedSchema = schema;
isSchemaValid = validator.loadSchema(parsedSchema);
console.log('Validator loaded schema. Is valid schema?', isSchemaValid);
}
}
$: {
if (schemaData !== undefined) {
data = schemaData;
isDataValid = validator.isValid(data);
console.log('Validator loaded data', validator.getErrors());
// Check if schema data is null
if (schemaData === null) schemaData = {};
isDataValid = validator.isValid(schemaData);
console.log('Validator loaded data. Errors?', validator.getErrors());
}
}
Expand All @@ -83,8 +82,8 @@
}
function initializeSchemaContext() {
if (validatedSchema !== undefined && isSchemaValid && isDataValid) {
schemaManager = new SchemaManager(validatedSchema, data);
if (parsedSchema !== undefined && isSchemaValid && isDataValid !== undefined) {
schemaManager = new SchemaManager(parsedSchema, schemaData);
setContext('schemaManager', schemaManager);
schemaManager.onPropertyChanges = (hasChanges) => {
unsavedChanges = hasChanges;
Expand All @@ -93,38 +92,64 @@
}
function saveChanges() {
console.log(data);
schemaManager.changesSaved();
const data = schemaManager.data;
// The following is required to remove all null values from the data object
// We suppose that null values are not valid, hence we remove them
const strippedNullData = Object.fromEntries(Object.entries(data).filter(([, v]) => v != null));
const isDataValid = validator.isValid(strippedNullData);
if (!isDataValid) {
if (handleValidationErrors !== null && handleValidationErrors !== undefined) {
handleValidationErrors(validator.getErrors());
}
console.error('Could not save changes. Data is invalid', validator.getErrors());
return;
}
if (handleSaveChanges !== null && handleSaveChanges !== undefined) {
handleSaveChanges(strippedNullData)
.then(() => {
schemaManager.changesSaved();
})
.catch((err) => {
console.error(err);
});
}
}
export function resetChanges(args) {
// Set schemaData to incoming args value
schemaData = args;
// Mark changes as unsaved
unsavedChanges = true;
}
</script>

<div>
<p>Component status</p>
<ul>
{#if unsavedChanges}
<li>Unsaved changes: {unsavedChanges}</li>
<li>
<button class='btn btn-success {unsavedChanges ? "" : "disabled"}' on:click={saveChanges}>
Save changes
</button>
</li>
{/if}
<li>Unsaved changes: {unsavedChanges}</li>
<li>
<button class='btn btn-success {unsavedChanges ? "" : "disabled"}' on:click={saveChanges}>
Save changes
</button>
</li>
</ul>
</div>

{#if validatedSchema !== undefined && isSchemaValid && isDataValid}
{#if parsedSchema !== undefined && isSchemaValid && isDataValid !== undefined}

<!-- Start rendering the schema structure -->
<div id='json-schema'>
{#key validatedSchema}
{#key schemaManager}
<PropertiesBlock
properties={validatedSchema.properties}
properties={parsedSchema.properties}
/>
{/key}
</div>

{:else if validatedSchema === undefined}
{:else if parsedSchema === undefined}

<div>
<p>Loading schema</p>
Expand All @@ -137,15 +162,15 @@
<p>Something is wrong</p>
</div>

{:else if !isDataValid && data !== undefined}
{:else if !isDataValid && schemaData !== undefined}


<div class='alert alert-danger'>
<div>Data object is invalid</div>
<div>Something is wrong</div>
</div>

{:else if data === undefined}
{:else if schemaData === undefined}

<div class='alert alert-warning'>
<span>Data object is missing</span>
Expand Down
3 changes: 2 additions & 1 deletion src/lib/components/common/jschema/schema_management.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export default class SchemaManager {
if (schemaData === undefined) {
throw new Error('Schema data is undefined');
}
this.data = schemaData;
// Deep copy the schema data
this.data = JSON.parse(JSON.stringify(schemaData));
}

getValue(key) {
Expand Down
65 changes: 65 additions & 0 deletions src/lib/components/workflow/ArgumentsSchema.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script>
import { page } from '$app/stores';
import JSchema from '$lib/components/common/jschema/JSchema.svelte';
import StandardErrorAlert from '$lib/components/common/StandardErrorAlert.svelte';
import { updateFormEntry } from '$lib/components/workflow/task_form_utils';
const SUPPORTED_SCHEMA_VERSIONS = ['pydantic_v1'];
export let workflowId = undefined;
export let workflowTaskId = undefined;
export let argumentsSchema = undefined;
export let argumentsSchemaVersion = undefined;
export let validSchema = undefined;
export let args = undefined;
let schemaComponent = undefined;
function handleSaveChanges(newArgs) {
return new Promise((resolve, reject) => {
const projectId = $page.params.projectId;
updateFormEntry(
projectId,
workflowId,
workflowTaskId,
newArgs,
'args'
).then(response => {
resolve(response.args);
}).catch(err => {
new StandardErrorAlert({
target: document.getElementById('json-schema-validation-errors'),
props: {
error: err
}
});
reject(err);
});
});
}
function handleValidationErrors(errors) {
new StandardErrorAlert({
target: document.getElementById('json-schema-validation-errors'),
props: {
error: errors
}
});
}
$: {
if (argumentsSchemaVersion && SUPPORTED_SCHEMA_VERSIONS.includes(argumentsSchemaVersion)) {
validSchema = true;
} else {
validSchema = false;
}
}
</script>

<div>
<div id='json-schema-validation-errors'></div>
<button on:click={() => { schemaComponent.resetChanges(args) }}>Reset</button>
<JSchema schema={argumentsSchema} schemaData={args} {handleSaveChanges} {handleValidationErrors}
bind:this={schemaComponent} />
</div>
41 changes: 23 additions & 18 deletions src/routes/projects/[projectId]/workflows/[workflowId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import ConfirmActionButton from '$lib/components/common/ConfirmActionButton.svelte';
import StandardErrorAlert from '$lib/components/common/StandardErrorAlert.svelte';
import MetaPropertiesForm from '$lib/components/workflow/MetaPropertiesForm.svelte';
import ArgumentsSchema from '$lib/components/workflow/ArgumentsSchema.svelte';
// Workflow
let workflow = undefined;
Expand All @@ -26,6 +27,8 @@
let inputDatasetControl = '';
let outputDatasetControl = '';
let workerInitControl = '';
let argsSchemaAvailable = undefined;
let argsSchemaValid = undefined;
$: updatableWorkflowList = workflow?.task_list || [];
Expand Down Expand Up @@ -139,6 +142,10 @@
const workflowTaskId = event.currentTarget.getAttribute('data-fs-target');
const wft = workflow.task_list.find((task) => task.id == workflowTaskId);
workflowTaskContext.set(wft);
// Check if args schema is available
argsSchemaAvailable = wft.task.args_schema === undefined || wft.task.args_schema === null ? false : true;
// Suppose args schema is valid
argsSchemaValid = true;
}
function moveWorkflowTask(index, direction) {
Expand Down Expand Up @@ -384,15 +391,6 @@
<li class='nav-item'>
<span class='nav-link disabled'>Info</span>
</li>
<li class='nav-item'>
<button
data-bs-toggle='tab'
data-bs-target='#experimental-tab'
class='nav-link'
>
Experimental
</button>
</li>
</ul>
{:else}
Select a workflow task from the list
Expand All @@ -403,11 +401,22 @@
<div class="card-body">
{#if selectedWorkflowTask}
{#key selectedWorkflowTask}
<ArgumentForm
workflowId={workflow.id}
workflowTaskId={selectedWorkflowTask.id}
workflowTaskArgs={selectedWorkflowTask.args}
/>
{#if argsSchemaAvailable && argsSchemaValid}
<ArgumentsSchema
workflowId={workflow.id}
workflowTaskId={selectedWorkflowTask.id}
argumentsSchema={selectedWorkflowTask.task.args_schema}
argumentsSchemaVersion={selectedWorkflowTask.task.args_schema_version}
args={selectedWorkflowTask.args}
bind:validSchema={argsSchemaValid}
></ArgumentsSchema>
{:else}
<ArgumentForm
workflowId={workflow.id}
workflowTaskId={selectedWorkflowTask.id}
workflowTaskArgs={selectedWorkflowTask.args}
/>
{/if}
{/key}
{/if}
</div>
Expand All @@ -425,10 +434,6 @@
{/if}
</div>
</div>
<div id='experimental-tab' class='tab-pane'>
<div class='card-body'>
</div>
</div>
</div>
</div>
</div>
Expand Down

0 comments on commit 45e7f19

Please sign in to comment.