Skip to content
Snippets Groups Projects
ArbitraryWorkflowView.vue 7.39 KiB
<script setup lang="ts">
import WorkflowDocumentationTabs from "@/components/workflows/WorkflowDocumentationTabs.vue";
import { onMounted, reactive, ref, watch } from "vue";
import { GitRepository, requiredRepositoryFiles } from "@/utils/GitRepository";
import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import ParameterSchemaFormComponent from "@/components/parameter-schema/ParameterSchemaFormComponent.vue";
import { WorkflowExecutionService } from "@/client/workflow";
import { useRouter } from "vue-router";
import { Toast } from "bootstrap";
import { useArbitraryWorkflowStore } from "@/stores/devWorkflow";
import type { WorkflowIn } from "@/client/workflow";

const props = defineProps<{
  wid: string;
}>();

const router = useRouter();
const workflowStore = useArbitraryWorkflowStore();

const workflowState = reactive<{
  workflow?: WorkflowIn;
  loading: boolean;
  changelogMarkdown?: string;
  descriptionMarkdown?: string;
  usageMarkdown?: string;
  outputMarkdown?: string;
  parameterSchema?: Record<string, never>;
  repo: GitRepository;
}>({
  loading: true,
  workflow: undefined,
  repo: GitRepository.buildRepository(
    "https://github.de/eample/example",
    "0123456789abcdef",
  ),
});

const workflowExecutionState = reactive<{
  loading: boolean;
  errorType?: string;
}>({
  loading: false,
  errorType: undefined,
});

const showDocumentation = ref<boolean>(true);
let errorToast: Toast | null = null;

function downloadVersionFiles() {
  if (workflowState.workflow) {
    workflowState.loading = true;
    Promise.all(
      requiredRepositoryFiles(workflowState.workflow.modes).map((file) =>
        workflowState.repo.downloadFile(file).then((response) => {
          if (file.includes("README")) {
            workflowState.descriptionMarkdown = response.data;
          } else if (file.includes("CHANGELOG")) {
            workflowState.changelogMarkdown = response.data;
          } else if (file.includes("usage")) {
            workflowState.usageMarkdown = response.data;
          } else if (file.includes("output")) {
            workflowState.outputMarkdown = response.data;
          } else {
            workflowState.parameterSchema = response.data;
          }
        }),
      ),
    ).finally(() => {
      workflowState.loading = false;
    });
  }
}

watch(
  () => workflowState.workflow,
  (newWorkflow, oldWorkflow) => {
    if (
      newWorkflow &&
      newWorkflow?.git_commit_hash !== oldWorkflow?.git_commit_hash
    ) {
      workflowState.repo = GitRepository.buildRepository(
        newWorkflow.repository_url,
        newWorkflow.git_commit_hash,
        newWorkflow.token ?? undefined,
      );
      downloadVersionFiles();
    }
  },
);

function startWorkflow(
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  parameters: Record<string, any>,
  notes?: string,
  report_output_bucket?: string,
) {
  if (workflowState.workflow) {
    errorToast?.hide();
    workflowExecutionState.loading = true;
    WorkflowExecutionService.workflowExecutionStartArbitraryWorkflow({
      git_commit_hash: workflowState.workflow.git_commit_hash,
      parameters: parameters,
      report_output_bucket: report_output_bucket,
      repository_url: workflowState.workflow.repository_url,
      token: workflowState.workflow.token ?? undefined,
      mode:
        (workflowState.workflow.modes ?? []).length > 0
          ? {
              name: "",
              // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
              entrypoint: workflowState.workflow.modes![0].entrypoint,
              // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
              schema_path: workflowState.workflow.modes![0].schema_path,
            }
          : undefined,
    })
      .then(() => {
        router.push({
          name: "workflow-executions",
        });
      })
      .catch((err) => {
        console.error(err);
        if (err.body["detail"].includes("workflow execution limit")) {
          workflowExecutionState.errorType = "limit";
        }
        errorToast?.show();
      })
      .finally(() => {
        workflowExecutionState.loading = false;
      });
  }
}

onMounted(() => {
  errorToast = new Toast("#arbitraryWorkflowExecutionViewErrorToast");
  workflowState.workflow = workflowStore.arbitraryWorkflows[props.wid];
});
</script>

<template>
  <div class="toast-container position-fixed top-toast end-0 p-3">
    <div
      role="alert"
      aria-live="assertive"
      aria-atomic="true"
      class="toast text-bg-danger align-items-center border-0"
      data-bs-autohide="true"
      id="arbitraryWorkflowExecutionViewErrorToast"
    >
      <div class="d-flex p-2 justify-content-between align-items-center">
        <div class="toast-body">
          <template v-if="workflowExecutionState.errorType === 'limit'">
            You have too many active workflow executions to start a new one.
          </template>
          <template v-else>
            There was an error with starting the workflow execution. Look in the
            console for more information.
          </template>
        </div>
        <button
          type="button"
          class="btn-close btn-close-white"
          data-bs-dismiss="toast"
          aria-label="Close"
        ></button>
      </div>
    </div>
  </div>
  <template v-if="workflowState.workflow">
    <div class="row m-1 border-bottom mb-4">
      <h1 class="mb-2">Arbitrary Workflow</h1>
    </div>
    <h4>
      Git Repository:
      <a target="_blank" :href="workflowState.workflow?.repository_url">{{
        workflowState.workflow?.repository_url
      }}</a>
    </h4>
    <h4 :class="{ 'mb-5': workflowState.workflow.modes!.length < 1 }">
      Git Commit Hash: {{ workflowState.workflow?.git_commit_hash }}
    </h4>
    <template v-if="workflowState.workflow.modes!.length > 0">
      <h5>Entrypoint: {{ workflowState.workflow?.modes?.[0].entrypoint }}</h5>
      <h5 class="mb-5">
        Schema File:
        {{ workflowState.workflow?.modes?.[0].schema_path }}
      </h5>
    </template>
    <div class="d-flex justify-content-center mb-5" v-if="showDocumentation">
      <a
        role="button"
        href="#"
        class="btn btn-success btn-lg mx-auto fs-4"
        :class="{ disabled: !workflowState.parameterSchema }"
        @click="showDocumentation = false"
      >
        <font-awesome-icon icon="fa-solid fa-rocket" class="me-2" />
        <span class="align-middle">Launch</span>
      </a>
    </div>
    <workflow-documentation-tabs
      v-if="showDocumentation"
      :loading="workflowState.loading"
      :output-markdown="workflowState.outputMarkdown"
      :usage-markdown="workflowState.usageMarkdown"
      :description-markdown="workflowState.descriptionMarkdown"
      :changelog-markdown="workflowState.changelogMarkdown"
      :parameter-schema="workflowState.parameterSchema"
    />
    <parameter-schema-form-component
      v-else
      :loading="workflowExecutionState.loading"
      :schema="workflowState.parameterSchema"
      @start-workflow="startWorkflow"
    />
  </template>
  <template v-else>
    <div class="text-center fs-1 mt-5">
      <font-awesome-icon
        icon="fa-solid fa-magnifying-glass"
        class="my-5 fs-0"
        style="color: var(--bs-secondary)"
      />
      <p class="my-5">Could not find your workflow.<br />Please re-enter it.</p>
      <router-link :to="{ name: 'workflows' }" class="mt-5">Back</router-link>
    </div>
  </template>
</template>

<style scoped></style>