Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented editing and deletion of tasks #262

Merged
merged 5 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

# Unreleased

* Implemented editing and deletion of tasks (\#207).
* Bump `word-wrap` from 1.2.3 to 1.2.5 (\#251).
* Implemented A to B workflow execution (\#254).
* Improved enable/disable button state (\#257).
Expand Down
22 changes: 22 additions & 0 deletions src/lib/common/component_utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,25 @@ export function fieldHasValue(event) {
const inputValue = event.target?.value || undefined;
return inputValue !== undefined && inputValue !== '';
}

export function getOnlyModifiedProperties(oldProperties, newProperties) {
const modifiedProperties = {};
for (let key in newProperties) {
if (newProperties[key] !== oldProperties[key]) {
modifiedProperties[key] = newProperties[key];
}
}
return modifiedProperties;
}

export function unsetEmptyStrings(inputValues) {
const clearedValues = {};
for (let key in inputValues) {
if (typeof(inputValues[key]) === 'string' && inputValues[key].trim() === '') {
clearedValues[key] = null;
} else {
clearedValues[key] = inputValues[key];
}
}
return clearedValues;
}
164 changes: 164 additions & 0 deletions src/lib/components/tasks/TaskEditModal.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<script>
import { originalTaskStore, taskStore } from '$lib/stores/taskStores';
import StandardErrorAlert from '$lib/components/common/StandardErrorAlert.svelte';
import { getOnlyModifiedProperties, unsetEmptyStrings } from '$lib/common/component_utilities';
export let updateEditedTask;
$: task = $taskStore;
$: originalTask = $originalTaskStore;
$: updateEnabled = task && task.name && task.command && task.input_type && task.output_type;
async function handleEditTask() {
let taskProperties = unsetEmptyStrings(task);
taskProperties = getOnlyModifiedProperties(originalTask, taskProperties);
const response = await fetch('/tasks/' + task.id, {
method: 'PATCH',
credentials: 'include',
body: JSON.stringify(taskProperties)
});
if (response.ok) {
console.log('Task updated successfully');
updateEditedTask(task);
// eslint-disable-next-line no-undef
const modal = bootstrap.Modal.getInstance(
document.getElementById('taskEditModal')
);
modal.hide();
} else {
new StandardErrorAlert({
target: document.getElementById('editTaskErrorAlert'),
props: {
error: await response.json()
}
});
}
}
async function undoChangesAndClose() {
for (let key in originalTask) {
task[key] = originalTask[key];
}
// eslint-disable-next-line no-undef
const modal = bootstrap.Modal.getInstance(
document.getElementById('taskEditModal')
);
modal.hide();
}
</script>

<div class="modal modal-xl" id="taskEditModal" data-bs-backdrop="static">
<div class="modal-dialog">
{#if task}
<div class="modal-content">
<div class="modal-header">
<h1 class="h5 modal-title">Task {task.name}</h1>
</div>
<div class="modal-body">
<div class="row mb-3">
<div class="col-12">
<p class="lead">Task properties</p>

<span id="editTaskErrorAlert"></span>

<div class="mb-2 row">
<label for="taskName" class="col-2 col-form-label text-end">Name</label>
<div class="col-10">
<input id="taskName" type="text" bind:value={task.name} class="form-control" class:is-invalid={!task.name}>
{#if !task.name}
<div class="invalid-feedback">Required field</div>
{/if}
</div>
</div>

<div class="mb-2 row">
<label for="version" class="col-2 col-form-label text-end">Version</label>
<div class="col-10">
<input id="version" type="text" bind:value={task.version} class="form-control">
</div>
</div>

<div class="mb-2 row">
<label for="owner" class="col-2 col-form-label text-end">Owner</label>
<div class="col-10">
<input id="owner" type="text" bind:value={task.owner} disabled class="form-control">
</div>
</div>

<div class="mb-2 row">
<label for="command" class="col-2 col-form-label text-end">Command</label>
<div class="col-10">
<input id="command" type="text" bind:value={task.command} class="form-control" class:is-invalid={!task.command}>
{#if !task.command}
<div class="invalid-feedback">Required field</div>
{/if}
</div>
</div>

<div class="mb-2 row">
<label for="source" class="col-2 col-form-label text-end">Source</label>
<div class="col-10">
<input id="source" type="text" bind:value={task.source} disabled class="form-control">
</div>
</div>

<div class="mb-2 row">
<label for="inputType" class="col-2 col-form-label text-end">Input Type</label>
<div class="col-10">
<input id="inputType" type="text" bind:value={task.input_type} class="form-control" class:is-invalid={!task.input_type}>
{#if !task.input_type}
<div class="invalid-feedback">Required field</div>
{/if}
</div>
</div>

<div class="mb-2 row">
<label for="outputType" class="col-2 col-form-label text-end">Output Type</label>
<div class="col-10">
<input id="outputType" type="text" bind:value={task.output_type} class="form-control" class:is-invalid={!task.output_type}>
{#if !task.output_type}
<div class="invalid-feedback">Required field</div>
{/if}
</div>
</div>

<div class="mb-2 row">
<label for="argsSchemaVersion" class="col-2 col-form-label text-end">Args Schema Version</label>
<div class="col-10">
<input id="ar$gsSchemaVersion" type="text" bind:value={task.args_schema_version} disabled class="form-control">
</div>
</div>

<div class="mb-2 row">
<label for="argsSchema" class="col-2 col-form-label text-end">Args Schema</label>
<div class="col-10">
<textarea name="argsSchema" value={JSON.stringify(task.args_schema, null, 2)} disabled class="form-control" rows="10"></textarea>
</div>
</div>

<div class="mb-2 row">
<label for="docsLink" class="col-2 col-form-label text-end">Docs Link</label>
<div class="col-10">
<input id="docsLink" type="text" bind:value={task.docs_link} disabled class="form-control">
</div>
</div>

<div class="mb-2 row">
<label for="docsInfo" class="col-2 col-form-label text-end">Docs Info</label>
<div class="col-10">
<textarea id="docsInfo" type="text" bind:value={task.docs_info} disabled class="form-control" rows="5"></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" on:click={handleEditTask} disabled={!updateEnabled}>Update</button>
<button class="btn btn-secondary" on:click={undoChangesAndClose}>Close</button>
</div>
</div>
{/if}
</div>
</div>
32 changes: 27 additions & 5 deletions src/lib/components/tasks/TaskInfoModal.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script>
// TaskInfoModal component
import { taskModal } from '$lib/stores/taskStores';
import { taskStore } from '$lib/stores/taskStores';
// Task to be displayed
// let task = {}
Expand All @@ -11,7 +11,7 @@
// taskModal.subscribe( taskId => {
// project =
// })
$: task = $taskModal;
$: task = $taskStore;
</script>

<div class="modal modal-xl" id="taskInfoModal">
Expand All @@ -29,9 +29,9 @@
<li class="list-group-item list-group-item-light fw-bold">Name</li>
<li class="list-group-item">{task?.name}</li>
<li class="list-group-item list-group-item-light fw-bold">Version</li>
<li class="list-group-item">{task?.version}</li>
<li class="list-group-item">{task?.version || '-'}</li>
<li class="list-group-item list-group-item-light fw-bold">Owner</li>
<li class="list-group-item">{task?.owner}</li>
<li class="list-group-item">{task?.owner || '-'}</li>
<li class="list-group-item list-group-item-light fw-bold">Command</li>
<li class='list-group-item'><code>{task?.command}</code></li>
<li class='list-group-item list-group-item-light fw-bold'>Source</li>
Expand All @@ -45,12 +45,34 @@
<pre>{task?.output_type}</pre>
</li>
<li class='list-group-item list-group-item-light fw-bold'>Args Schema Version</li>
<li class='list-group-item'>{task?.args_schema_version}</li>
<li class='list-group-item'>{task?.args_schema_version || '-'}</li>
<li class='list-group-item list-group-item-light fw-bold'>Args Schema</li>
<li class='list-group-item'>
{#if task?.args_schema}
<code>
<pre>{JSON.stringify(task?.args_schema, null, 2)}</pre>
</code>
{:else}
-
{/if}
</li>
<li class='list-group-item list-group-item-light fw-bold'>Docs Link</li>
<li class='list-group-item'>
{#if task?.docs_link}
<a href="{task?.docs_link}" target="_blank">{task?.docs_link}</a>
{:else}
-
{/if}
</li>
<li class='list-group-item list-group-item-light fw-bold'>Docs Info</li>
<li class='list-group-item'>
{#if task?.docs_info}
<code>
<pre>{task?.docs_info}</pre>
</code>
{:else}
-
{/if}
</li>
</ul>
</div>
Expand Down
14 changes: 3 additions & 11 deletions src/lib/components/workflow/MetaPropertiesForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import { updateFormEntry } from '$lib/components/workflow/task_form_utils';
import FormBuilder from '$lib/components/workflow/common/FormBuilder.svelte';
import StandardErrorAlert from '$lib/components/common/StandardErrorAlert.svelte';
import { getOnlyModifiedProperties } from '$lib/common/component_utilities';

// Workflow id
export let workflowId;
Expand All @@ -29,11 +30,12 @@
async function handleEntryUpdate() {
const projectId = $page.params.projectId;
try {
const modifiedProperties = getOnlyModifiedProperties(originalMetaProperties, metaProperties);
const updatedMetaProperties = await updateFormEntry(
projectId,
workflowId,
taskId,
getOnlyModifiedProperties(),
modifiedProperties,
'meta'
);
metaProperties = updatedMetaProperties.meta;
Expand All @@ -51,16 +53,6 @@
});
}
}

function getOnlyModifiedProperties() {
const modifiedProperties = {};
for (let key in metaProperties) {
if (metaProperties[key] !== originalMetaProperties[key]) {
modifiedProperties[key] = metaProperties[key];
}
}
return modifiedProperties;
}
</script>

<div>
Expand Down
56 changes: 55 additions & 1 deletion src/lib/server/api/v1/task_api.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FRACTAL_SERVER_HOST } from '$env/static/private';
import { PostResourceException } from '$lib/common/errors.js';
import { PostResourceException, responseError } from '$lib/common/errors.js';

/**
* Fetches a list of tasks from the server
Expand Down Expand Up @@ -42,6 +42,7 @@ export async function createTask(fetch, formData) {
const requestData = {
name: formData.get('name'),
command: formData.get('command'),
version: formData.get('version'),
source: formData.get('source'),
input_type: formData.get('input_type'),
output_type: formData.get('output_type')
Expand Down Expand Up @@ -136,3 +137,56 @@ export async function taskCollectionStatus(fetch, taskId) {

throw new Error('Unable to fetch collection operation status');
}


/**
* Deletes a task from the server
* @param fetch
* @param taskId
* @returns {Promise<*>}
*/
export async function deleteTask(fetch, taskId) {
const response = await fetch(
FRACTAL_SERVER_HOST + `/api/v1/task/${taskId}`,
{
method: 'DELETE',
credentials: 'include',
mode: 'cors'
}
);

if (response.ok) {
return new Response(null, { status: response.status });
}

await responseError(response);
}


/**
* Edits a task on the server
* @param fetch
* @param taskId
* @returns {Promise<*>}
*/
export async function editTask(fetch, taskId, task) {
const headers = new Headers();
headers.append('Content-Type', 'application/json');

const response = await fetch(
FRACTAL_SERVER_HOST + `/api/v1/task/${taskId}`,
{
method: 'PATCH',
credentials: 'include',
mode: 'cors',
headers,
body: JSON.stringify(task)
}
);

if (response.ok) {
return new Response(null, { status: response.status });
}

await responseError(response);
}
3 changes: 2 additions & 1 deletion src/lib/stores/taskStores.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { writable } from 'svelte/store';

export const taskModal = writable(undefined);
export const taskStore = writable(undefined);
export const originalTaskStore = writable(undefined);
export const modalTaskCollectionId = writable(undefined);
Loading