From 81ef48978ad10dfb90596e702f9b71b7b1200346 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de>
Date: Wed, 19 Jun 2024 14:11:11 +0000
Subject: [PATCH] Add option to select the nextflow version for workflow
 versions

#131
---
 src/client/index.ts                           |  1 +
 src/client/models/DevWorkflowExecutionIn.ts   |  5 +++
 src/client/models/NextflowVersion.ts          | 18 +++++++++
 src/client/models/UserOutExtended.ts          |  2 +-
 src/client/models/WorkflowIn.ts               |  5 +++
 src/client/models/WorkflowUpdate.ts           |  5 +++
 src/client/models/WorkflowVersion.ts          |  5 +++
 src/components/AppHeader.vue                  |  7 ----
 .../workflows/WorkflowWithVersionsCard.vue    |  6 ++-
 .../modals/ArbitraryWorkflowModal.vue         | 26 ++++++++++++-
 .../workflows/modals/CreateWorkflowModal.vue  | 30 ++++++++++++++-
 .../workflows/modals/UpdateWorkflowModal.vue  | 37 +++++++++++++++----
 src/views/admin/AdminUsersView.vue            |  6 ---
 src/views/workflows/ArbitraryWorkflowView.vue |  4 +-
 src/views/workflows/MyWorkflowsView.vue       | 10 ++++-
 15 files changed, 138 insertions(+), 29 deletions(-)
 create mode 100644 src/client/models/NextflowVersion.ts

diff --git a/src/client/index.ts b/src/client/index.ts
index 98752ac..77aaffd 100644
--- a/src/client/index.ts
+++ b/src/client/index.ts
@@ -26,6 +26,7 @@ export type { ErrorDetail } from './models/ErrorDetail';
 export { FileTree } from './models/FileTree';
 export type { HTTPValidationError } from './models/HTTPValidationError';
 export type { IconUpdateOut } from './models/IconUpdateOut';
+export { NextflowVersion } from './models/NextflowVersion';
 export { OIDCProvider } from './models/OIDCProvider';
 export type { ParameterExtension } from './models/ParameterExtension';
 export { Permission } from './models/Permission';
diff --git a/src/client/models/DevWorkflowExecutionIn.ts b/src/client/models/DevWorkflowExecutionIn.ts
index f65f6eb..e075238 100644
--- a/src/client/models/DevWorkflowExecutionIn.ts
+++ b/src/client/models/DevWorkflowExecutionIn.ts
@@ -2,6 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
+import type { NextflowVersion } from './NextflowVersion';
 import type { WorkflowModeIn } from './WorkflowModeIn';
 export type DevWorkflowExecutionIn = {
     /**
@@ -36,5 +37,9 @@ export type DevWorkflowExecutionIn = {
      * Mode of the workflow with an alternative entrypoint
      */
     mode?: (WorkflowModeIn | null);
+    /**
+     * The version of Nextflow this workflow execution requires
+     */
+    nextflow_version: NextflowVersion;
 };
 
diff --git a/src/client/models/NextflowVersion.ts b/src/client/models/NextflowVersion.ts
new file mode 100644
index 0000000..e16dfe9
--- /dev/null
+++ b/src/client/models/NextflowVersion.ts
@@ -0,0 +1,18 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export enum NextflowVersion {
+    _23_04_0 = '23.04.0',
+    _23_04_1 = '23.04.1',
+    _23_04_2 = '23.04.2',
+    _23_04_3 = '23.04.3',
+    _23_04_4 = '23.04.4',
+    _23_04_5 = '23.04.5',
+    _23_10_0 = '23.10.0',
+    _23_10_1 = '23.10.1',
+    _23_10_2 = '23.10.2',
+    _23_10_3 = '23.10.3',
+    _24_04_1 = '24.04.1',
+    _24_04_2 = '24.04.2',
+}
diff --git a/src/client/models/UserOutExtended.ts b/src/client/models/UserOutExtended.ts
index a05ecb6..709361b 100644
--- a/src/client/models/UserOutExtended.ts
+++ b/src/client/models/UserOutExtended.ts
@@ -27,6 +27,6 @@ export type UserOutExtended = {
     /**
      * URL to the gravatar avatar based on the users email
      */
-    gravatar_url?: (string | null);
+    gravatar_url: string;
 };
 
diff --git a/src/client/models/WorkflowIn.ts b/src/client/models/WorkflowIn.ts
index acc74b5..f433bf9 100644
--- a/src/client/models/WorkflowIn.ts
+++ b/src/client/models/WorkflowIn.ts
@@ -2,6 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
+import type { NextflowVersion } from './NextflowVersion';
 import type { WorkflowModeIn } from './WorkflowModeIn';
 export type WorkflowIn = {
     /**
@@ -32,5 +33,9 @@ export type WorkflowIn = {
      * List of modes with alternative entrypoint the new workflow has
      */
     modes?: Array<WorkflowModeIn>;
+    /**
+     * The version of Nextflow this workflow version requires
+     */
+    nextflow_version: NextflowVersion;
 };
 
diff --git a/src/client/models/WorkflowUpdate.ts b/src/client/models/WorkflowUpdate.ts
index 9b3dac3..a67749a 100644
--- a/src/client/models/WorkflowUpdate.ts
+++ b/src/client/models/WorkflowUpdate.ts
@@ -2,6 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
+import type { NextflowVersion } from './NextflowVersion';
 import type { WorkflowModeIn } from './WorkflowModeIn';
 export type WorkflowUpdate = {
     /**
@@ -20,5 +21,9 @@ export type WorkflowUpdate = {
      * Delete modes for the new workflow version.
      */
     delete_modes?: Array<string>;
+    /**
+     * The version of Nextflow this new workflow version requires.
+     */
+    nextflow_version: NextflowVersion;
 };
 
diff --git a/src/client/models/WorkflowVersion.ts b/src/client/models/WorkflowVersion.ts
index 1974ff1..3e3138e 100644
--- a/src/client/models/WorkflowVersion.ts
+++ b/src/client/models/WorkflowVersion.ts
@@ -2,6 +2,7 @@
 /* istanbul ignore file */
 /* tslint:disable */
 /* eslint-disable */
+import type { NextflowVersion } from './NextflowVersion';
 import type { ParameterExtension } from './ParameterExtension';
 import type { WorkflowVersionStatus } from './WorkflowVersionStatus';
 export type WorkflowVersion = {
@@ -37,5 +38,9 @@ export type WorkflowVersion = {
      * Parameter extension specific for this CloWM instance
      */
     parameter_extension?: (ParameterExtension | null);
+    /**
+     * The version of Nextflow this workflow version requires
+     */
+    nextflow_version: NextflowVersion;
 };
 
diff --git a/src/components/AppHeader.vue b/src/components/AppHeader.vue
index 97d73da..331c790 100644
--- a/src/components/AppHeader.vue
+++ b/src/components/AppHeader.vue
@@ -3,7 +3,6 @@ import { useUserStore } from "@/stores/users";
 import { useRoute, useRouter } from "vue-router";
 import { watch, ref, computed } from "vue";
 import { OpenAPI } from "@/client";
-import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 
 const userRepository = useUserStore();
 const route = useRoute();
@@ -266,18 +265,12 @@ watch(
         >
           <strong class="me-2">{{ userRepository.user.display_name }}</strong>
           <img
-            v-if="userRepository.user.gravatar_url"
             :src="userRepository.user.gravatar_url + '?d=mp&s=32'"
             class="rounded-circle"
             height="32"
             width="32"
             alt="profile picture"
           />
-          <font-awesome-icon
-            v-else
-            icon="fa-solid fa-circle-user"
-            class="text-secondary fs-4"
-          />
         </a>
         <ul
           class="dropdown-menu text-small shadow"
diff --git a/src/components/workflows/WorkflowWithVersionsCard.vue b/src/components/workflows/WorkflowWithVersionsCard.vue
index cae0ee5..a5a95f2 100644
--- a/src/components/workflows/WorkflowWithVersionsCard.vue
+++ b/src/components/workflows/WorkflowWithVersionsCard.vue
@@ -215,6 +215,7 @@ onMounted(() => {
                 <th scope="col">Version</th>
                 <th scope="col">Status</th>
                 <th scope="col">Updated at</th>
+                <th scope="col">Nextflow version</th>
                 <th scope="col" class="text-align-center">Usage</th>
                 <th scope="col" class="text-align-center">Icon</th>
                 <th scope="col"></th>
@@ -246,6 +247,9 @@ onMounted(() => {
                 <td>
                   {{ dayjs.unix(version.created_at).format("DD MMM YYYY") }}
                 </td>
+                <td class="text-align-center">
+                  {{ version.nextflow_version }}
+                </td>
                 <td class="text-align-center">
                   <span
                     class="text-success me-1"
@@ -351,7 +355,7 @@ onMounted(() => {
               </tr>
               <tr>
                 <th scope="row" class="fw-bold">Overall</th>
-                <td colspan="2"></td>
+                <td colspan="3"></td>
                 <td class="text-align-center">
                   <span
                     class="text-success me-1"
diff --git a/src/components/workflows/modals/ArbitraryWorkflowModal.vue b/src/components/workflows/modals/ArbitraryWorkflowModal.vue
index 6399ce3..749dcd9 100644
--- a/src/components/workflows/modals/ArbitraryWorkflowModal.vue
+++ b/src/components/workflows/modals/ArbitraryWorkflowModal.vue
@@ -10,7 +10,7 @@ import {
 } from "@/utils/GitRepository";
 import { Collapse, Modal } from "bootstrap";
 import { useWorkflowStore } from "@/stores/workflows";
-import type { WorkflowModeOut } from "@/client";
+import { NextflowVersion, type WorkflowModeOut } from "@/client";
 import { environment } from "@/environment";
 import { useS3KeyStore } from "@/stores/s3keys";
 import { useResourceStore } from "@/stores/resources";
@@ -38,9 +38,12 @@ const resourceStore = useResourceStore();
 const workflow = reactive<{
   repository_url: string;
   git_commit_hash: string;
+  nextflow_version: NextflowVersion;
 }>({
   repository_url: "",
   git_commit_hash: "",
+  nextflow_version:
+    Object.values(NextflowVersion)[Object.values(NextflowVersion).length - 1],
 });
 
 const repositoryCredentials = reactive<{
@@ -504,7 +507,7 @@ onMounted(() => {
             />
           </div>
           <div id="gitRepoProviderHelp" class="form-text">
-            We support Github and GitLab Repositories
+            We support Github and (self-hosted) GitLab Repositories
           </div>
           <div class="text-danger">
             <div v-if="formState.unsupportedRepository">
@@ -555,6 +558,25 @@ onMounted(() => {
             </li>
           </ul>
         </div>
+        <div class="mb-3">
+          <label for="workflowNextflowVersionInput" class="form-label"
+            >Nextflow version</label
+          >
+          <select
+            class="form-select"
+            id="workflowNextflowVersionInput"
+            required
+            v-model="workflow.nextflow_version"
+          >
+            <option
+              v-for="version in Object.values(NextflowVersion).reverse()"
+              :key="version"
+              :value="version"
+            >
+              {{ version }}
+            </option>
+          </select>
+        </div>
         <div class="mb-3">
           <div class="form-check fs-5">
             <input
diff --git a/src/components/workflows/modals/CreateWorkflowModal.vue b/src/components/workflows/modals/CreateWorkflowModal.vue
index 61398ea..c1ec15a 100644
--- a/src/components/workflows/modals/CreateWorkflowModal.vue
+++ b/src/components/workflows/modals/CreateWorkflowModal.vue
@@ -1,7 +1,12 @@
 <script setup lang="ts">
 import { computed, onMounted, reactive, ref, watch } from "vue";
 import { Modal, Toast, Collapse, Tooltip } from "bootstrap";
-import type { WorkflowIn, WorkflowOut, WorkflowModeOut } from "@/client";
+import {
+  type WorkflowIn,
+  type WorkflowOut,
+  type WorkflowModeOut,
+  NextflowVersion,
+} from "@/client";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import { ApiError } from "@/client";
@@ -61,6 +66,8 @@ const workflow = reactive<WorkflowIn>({
   initial_version: undefined,
   token: undefined,
   modes: [],
+  nextflow_version:
+    Object.values(NextflowVersion)[Object.values(NextflowVersion).length - 1],
 });
 
 const formState = reactive<{
@@ -410,7 +417,7 @@ onMounted(() => {
             </div>
           </div>
           <div id="gitRepoProviderHelp" class="form-text">
-            We support GitHub and GitLab Repositories
+            We support GitHub and (self-hosted) GitLab Repositories
           </div>
           <div class="text-danger">
             <div v-if="formState.unsupportedRepository">
@@ -501,6 +508,25 @@ onMounted(() => {
             </div>
           </div>
         </div>
+        <div class="mb-3">
+          <label for="createWorkflowNextflowVersionInput" class="form-label"
+            >Nextflow version</label
+          >
+          <select
+            class="form-select"
+            id="createWorkflowNextflowVersionInput"
+            required
+            v-model="workflow.nextflow_version"
+          >
+            <option
+              v-for="version in Object.values(NextflowVersion).reverse()"
+              :key="version"
+              :value="version"
+            >
+              {{ version }}
+            </option>
+          </select>
+        </div>
         <div class="mb-3">
           <div class="form-check fs-5">
             <input
diff --git a/src/components/workflows/modals/UpdateWorkflowModal.vue b/src/components/workflows/modals/UpdateWorkflowModal.vue
index 55d13e7..7347855 100644
--- a/src/components/workflows/modals/UpdateWorkflowModal.vue
+++ b/src/components/workflows/modals/UpdateWorkflowModal.vue
@@ -1,13 +1,14 @@
 <script setup lang="ts">
 import { computed, onMounted, reactive, ref, watch } from "vue";
 import { Collapse, Modal, Toast } from "bootstrap";
-import type {
-  WorkflowUpdate,
-  WorkflowOut,
-  WorkflowModeIn,
-  WorkflowModeOut,
-  WorkflowVersion,
-  ApiError,
+import {
+  type WorkflowUpdate,
+  type WorkflowOut,
+  type WorkflowModeIn,
+  type WorkflowModeOut,
+  type WorkflowVersion,
+  type ApiError,
+  NextflowVersion,
 } from "@/client";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
@@ -59,6 +60,8 @@ const workflowUpdate = reactive<WorkflowUpdate>({
   git_commit_hash: "",
   delete_modes: [],
   append_modes: [],
+  nextflow_version:
+    Object.values(NextflowVersion)[Object.values(NextflowVersion).length - 1],
 });
 
 const workflowModes = reactive<{
@@ -96,6 +99,7 @@ watch(
   () => {
     resetForm();
     formState.modesEnabled = (latestVersion.value.modes ?? []).length > 0;
+    workflowUpdate.nextflow_version = latestVersion.value.nextflow_version;
     if (props.workflow.private) {
       formState.loadCredentials = true;
       WorkflowCredentialsService.workflowCredentialsGetWorkflowCredentials(
@@ -431,6 +435,25 @@ onMounted(() => {
             </div>
           </div>
         </div>
+        <div class="mb-3">
+          <label for="updateWorkflowNextflowVersionInput" class="form-label"
+            >Nextflow version</label
+          >
+          <select
+            class="form-select"
+            id="updateWorkflowNextflowVersionInput"
+            required
+            v-model="workflowUpdate.nextflow_version"
+          >
+            <option
+              v-for="version in Object.values(NextflowVersion).reverse()"
+              :key="version"
+              :value="version"
+            >
+              {{ version }}
+            </option>
+          </select>
+        </div>
         <div class="mb-3">
           <div class="form-check fs-5">
             <input
diff --git a/src/views/admin/AdminUsersView.vue b/src/views/admin/AdminUsersView.vue
index 69fa6ea..498e36f 100644
--- a/src/views/admin/AdminUsersView.vue
+++ b/src/views/admin/AdminUsersView.vue
@@ -213,18 +213,12 @@ onMounted(() => {
       <tr v-for="(user, index) in userState.users" :key="user.uid">
         <th scope="row">
           <img
-            v-if="user.gravatar_url"
             :src="user.gravatar_url + '?d=mp&s=32'"
             class="rounded-circle"
             height="32"
             width="32"
             alt="profile picture"
           />
-          <font-awesome-icon
-            v-else
-            icon="fa-solid fa-circle-user"
-            class="text-secondary fs-4"
-          />
           <span class="ms-2">{{ user.display_name }}</span>
         </th>
         <td>{{ user.uid }}</td>
diff --git a/src/views/workflows/ArbitraryWorkflowView.vue b/src/views/workflows/ArbitraryWorkflowView.vue
index 45f1b3e..3a58992 100644
--- a/src/views/workflows/ArbitraryWorkflowView.vue
+++ b/src/views/workflows/ArbitraryWorkflowView.vue
@@ -121,6 +121,7 @@ function startWorkflow(
         debug_s3_path: metaParameters.debug_s3_path,
         provenance_s3_path: metaParameters.provenance_s3_path,
         repository_url: workflowState.workflow.repository_url,
+        nextflow_version: workflowState.workflow.nextflow_version,
         token: workflowState.workflow.token ?? undefined,
         mode:
           (workflowState.workflow.modes ?? []).length > 0
@@ -185,8 +186,9 @@ onMounted(() => {
         workflowState.workflow?.repository_url
       }}</a>
     </h5>
+    <h5>Git Commit Hash: {{ workflowState.workflow?.git_commit_hash }}</h5>
     <h5 :class="{ 'mb-5': workflowState.workflow.modes!.length < 1 }">
-      Git Commit Hash: {{ workflowState.workflow?.git_commit_hash }}
+      Nextflow version: {{ workflowState.workflow?.nextflow_version }}
     </h5>
     <template v-if="workflowState.workflow.modes!.length > 0">
       <h5>Entrypoint: {{ workflowState.workflow?.modes?.[0].entrypoint }}</h5>
diff --git a/src/views/workflows/MyWorkflowsView.vue b/src/views/workflows/MyWorkflowsView.vue
index 9ad162d..7ccfeb4 100644
--- a/src/views/workflows/MyWorkflowsView.vue
+++ b/src/views/workflows/MyWorkflowsView.vue
@@ -1,7 +1,11 @@
 <script setup lang="ts">
 import { computed, onMounted, reactive } from "vue";
-import type { WorkflowOut, WorkflowVersion } from "@/client";
-import { WorkflowVersionStatus } from "@/client";
+import {
+  NextflowVersion,
+  type WorkflowOut,
+  type WorkflowVersion,
+  WorkflowVersionStatus,
+} from "@/client";
 import WorkflowWithVersionsCard from "@/components/workflows/WorkflowWithVersionsCard.vue";
 import CreateWorkflowModal from "@/components/workflows/modals/CreateWorkflowModal.vue";
 import CardTransitionGroup from "@/components/transitions/CardTransitionGroup.vue";
@@ -37,6 +41,7 @@ const workflowsState = reactive<{
         workflow_id: "",
         icon_url: "",
         modes: [],
+        nextflow_version: NextflowVersion._24_04_2,
       },
     ],
     repository_url: "",
@@ -52,6 +57,7 @@ const workflowsState = reactive<{
     icon_url: null,
     created_at: 0,
     status: WorkflowVersionStatus.CREATED,
+    nextflow_version: NextflowVersion._24_04_2,
   },
 });
 
-- 
GitLab