Skip to content

Commit

Permalink
Merge pull request #450 from fractal-analytics-platform/task-v1v2-com…
Browse files Browse the repository at this point in the history
…patibility

Tasks V1/V2 compatibility
  • Loading branch information
tcompa authored Apr 15, 2024
2 parents 5400bf3 + bfcf950 commit 44e13ea
Show file tree
Hide file tree
Showing 39 changed files with 1,395 additions and 799 deletions.
19 changes: 11 additions & 8 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

# Unreleased

* Supported fractal-server API V2 (\#434):
* added menu switch to support legacy and current API;
* Dataset V2 CRUD with attribute and type filters;
* new Dataset page with image list and filters;
* updated Single Task form to handle parallel and non parallel fields;
* updated workflow task form to handle parallel and non parallel arguments;
* handled V2 import and export of workflow task arguments;
* handled V2 version workflow task version update;
* Supported fractal-server API V2:
* added menu switch to support legacy and current API (\#434);
* Dataset V2 CRUD with attribute and type filters (\#434);
* new Dataset page with image list and filters (\#434);
* updated Single Task form to handle parallel and non parallel fields (\#434);
* updated workflow task form to handle parallel and non parallel arguments (\#434);
* handled V2 import and export of workflow task arguments (\#434);
* handled V2 version workflow task version update (\#434);
* added admin "Tasks V1/V2 compatibility" page (\#450);
* supported adding V1 task in V2 workflow (\#450);
* removed read only property from V2 datasets (\#450);

# 0.10.2

Expand Down
4 changes: 0 additions & 4 deletions __tests__/v2/CreateUpdateDatasetModal.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ describe('CreateUpdateDatasetModal', () => {
expect.objectContaining({
body: JSON.stringify({
name: 'my dataset',
read_only: false,
zarr_dir: '/tmp',
filters: {
attributes: { 'my-key': 'my-value' },
Expand Down Expand Up @@ -105,7 +104,6 @@ describe('CreateUpdateDatasetModal', () => {
expect.objectContaining({
body: JSON.stringify({
name: 'my dataset',
read_only: false,
zarr_dir: '/tmp',
filters: {
attributes: { 'my-key': 123 },
Expand Down Expand Up @@ -135,7 +133,6 @@ describe('CreateUpdateDatasetModal', () => {
expect.objectContaining({
body: JSON.stringify({
name: 'my dataset',
read_only: false,
zarr_dir: '/tmp',
filters: {
attributes: {},
Expand Down Expand Up @@ -166,7 +163,6 @@ describe('CreateUpdateDatasetModal', () => {
expect.objectContaining({
body: JSON.stringify({
name: 'my dataset',
read_only: false,
zarr_dir: '/tmp',
filters: {
attributes: {},
Expand Down
2 changes: 1 addition & 1 deletion playwright.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default defineConfig({

webServer: [
{
command: './tests/start-test-server.sh 2.0.0a3',
command: './tests/start-test-server.sh 2.0.0a6',
port: 8000,
waitForPort: true,
stdout: 'pipe',
Expand Down
142 changes: 142 additions & 0 deletions src/lib/components/tasks/TasksTable.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<script>
/**
* @type {any[]}
*/
export let tasks;
/**
* @param {number} index
*/
function isOldVersion(index) {
if (index === 0) {
return false;
}
const currentTask = tasks[index];
const previousTask = tasks[index - 1];
return previousTask.name === currentTask.name && previousTask.owner === currentTask.owner;
}
/**
* @param {number} index
*/
function isLastOldVersion(index) {
if (!isOldVersion(index)) {
return false;
}
if (index === tasks.length - 1) {
return false;
}
const currentTask = tasks[index];
const nextTask = tasks[index + 1];
return nextTask.name !== currentTask.name || nextTask.owner !== currentTask.owner;
}
/**
* @param {number} index
*/
function isMainVersion(index) {
if (isOldVersion(index)) {
return false;
}
if (index === tasks.length - 1) {
return false;
}
const currentTask = tasks[index];
const nextTask = tasks[index + 1];
return nextTask.name === currentTask.name && nextTask.owner === currentTask.owner;
}
/**
* @param {Event} event
*/
function handleToggleOldVersions(event) {
const element = /** @type {HTMLElement} */ (event.target);
/** @type {HTMLElement|null} */
let row = /** @type {HTMLElement} */ (element.closest('tr'));
if (!row.classList.contains('expanded')) {
closeAllOldVersions(/** @type {HTMLElement} */ (row.closest('table')));
}
toggleOldVersions(row);
}
/**
* @param {HTMLElement} table
*/
function closeAllOldVersions(table) {
const rows = table.querySelectorAll('tr');
for (const row of rows) {
if (row.classList.contains('expanded')) {
toggleOldVersions(row);
}
}
}
/**
* @param {HTMLElement} mainRow
*/
function toggleOldVersions(mainRow) {
mainRow.classList.toggle('expanded');
/** @type {HTMLElement|null} */
let row = mainRow;
while ((row = /** @type {HTMLElement|null} */ (row?.nextSibling))) {
if (!row.classList) {
continue;
}
if (row.classList.contains('old-version')) {
row.classList.toggle('collapsed');
} else {
break;
}
}
}
</script>
<table class="table align-middle">
<slot name="thead" />
<tbody>
{#key tasks}
{#each tasks as task, i}
<tr
class:old-version={isOldVersion(i)}
class:last-old-version={isLastOldVersion(i)}
class:is-main-version={isMainVersion(i)}
class:collapsed={isOldVersion(i)}
>
<slot name="custom-columns-left" {task} />
<td>{isOldVersion(i) ? '' : task.name}</td>
<td>
{task.version || ''}
{#if isMainVersion(i)}
<button class="btn btn-link" on:click={handleToggleOldVersions} aria-label="Expand old versions">
<i class="bi bi-plus-circle" />
</button>
{/if}
</td>
<slot name="custom-columns-right" {task} />
</tr>
{/each}
{/key}
</tbody>
</table>
<style>
:global(.is-main-version.expanded td) {
border-bottom-style: dashed;
}
:global(.old-version.collapsed) {
display: none;
}
:global(.old-version) {
display: table-row;
}
:global(.old-version td) {
border-bottom-style: dashed;
}
:global(.last-old-version td) {
border-bottom-style: solid;
}
</style>
61 changes: 61 additions & 0 deletions src/lib/components/v1/workflow/TaskInfoTab.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<script>
import { formatMarkdown } from '$lib/common/component_utilities';
/** @type {import("$lib/types").Task} */
export let task;
</script>

<ul class="list-group">
<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 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}
{@html formatMarkdown(task.docs_info)}
{:else}
-
{/if}
</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 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>
<li class="list-group-item">
<code>{task.source}</code>
</li>
<li class="list-group-item list-group-item-light fw-bold">Input Type</li>
<li class="list-group-item">
<code>{task.input_type}</code>
</li>
<li class="list-group-item list-group-item-light fw-bold">Output Type</li>
<li class="list-group-item">
<code>{task.output_type}</code>
</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 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>
</ul>
48 changes: 26 additions & 22 deletions src/lib/components/v1/workflow/VersionUpdate.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
let selectedUpdateVersion = '';
let originalArgs = '';
let argsToBeFixed = '';
let needChanges = false;
let argsToBeFixedValidJson = true;
/** @type {import('ajv').ErrorObject[] | null} */
let validationErrors = null;
Expand Down Expand Up @@ -111,6 +112,7 @@
validationErrors = null;
} else {
argsToBeFixed = JSON.stringify(args, null, 2);
needChanges = true;
validationErrors = validator.getErrors();
}
}
Expand Down Expand Up @@ -243,32 +245,34 @@
</ul>
</div>
{/if}
{#if argsToBeFixed}
{#if originalArgs}
{#if !validationErrors}
<div class="alert alert-success mt-3">The arguments are valid</div>
{/if}
<label class="form-label" for="fix-arguments">Fix the arguments:</label>
<textarea
class="form-control"
id="fix-arguments"
class:is-invalid={!argsToBeFixedValidJson}
bind:value={argsToBeFixed}
rows="20"
/>
{#if !argsToBeFixedValidJson}
<div class="invalid-feedback">Invalid JSON</div>
{#if needChanges}
<label class="form-label" for="fix-arguments">Fix the arguments:</label>
<textarea
class="form-control"
id="fix-arguments"
class:is-invalid={!argsToBeFixedValidJson}
bind:value={argsToBeFixed}
rows="20"
/>
{#if !argsToBeFixedValidJson}
<div class="invalid-feedback">Invalid JSON</div>
{/if}
<button type="button" class="btn btn-warning mt-3" on:click={check}> Check </button>
&nbsp;
<button
type="button"
class="btn btn-secondary mt-3"
on:click={cancel}
disabled={argsToBeFixed === originalArgs}
>
Cancel
</button>
&nbsp;
{/if}
<button type="button" class="btn btn-warning mt-3" on:click={check}> Check </button>
&nbsp;
<button
type="button"
class="btn btn-secondary mt-3"
on:click={cancel}
disabled={argsToBeFixed === originalArgs}
>
Cancel
</button>
&nbsp;
{/if}
<button type="button" class="btn btn-primary mt-3" on:click={update} disabled={!canBeUpdated}>
Update
Expand Down
11 changes: 6 additions & 5 deletions src/lib/components/v1/workflow/version-checker.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ import { AlertError } from '$lib/common/errors';

/**
* @param {import('$lib/types').Task} task
* @param {boolean|false=} v2
* @returns {Promise<import('$lib/types').Task[]>} the list of update candidates for the given task
*/
export async function getNewVersions(task) {
const updateCandidates = await getAllNewVersions([task]);
export async function getNewVersions(task, v2) {
const updateCandidates = await getAllNewVersions([task], v2);
return updateCandidates[task.id];
}

/**
* @param {import('$lib/types').Task[]} tasks
* @param {boolean|false=} v2
* @returns {Promise<{ [id: string]: import('$lib/types').Task[] }>} the list of update candidates, for each task received as input
*/
export async function getAllNewVersions(tasks) {
console.log('Checking for new versions');
const response = await fetch(`/api/v1/task`);
export async function getAllNewVersions(tasks, v2 = false) {
const response = await fetch(v2 ? `/api/v2/task-legacy` : `/api/v1/task`);

if (!response.ok) {
throw new AlertError(await response.json());
Expand Down
Loading

0 comments on commit 44e13ea

Please sign in to comment.