From e7413508a22f28c442c56dbb28bbc7930b9eeacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de> Date: Thu, 14 Sep 2023 10:19:01 +0200 Subject: [PATCH] Support updating private workflows #61 --- src/client/workflow/index.ts | 2 + .../workflow/models/DevWorkflowExecutionIn.ts | 4 - src/client/workflow/models/IconUpdateOut.ts | 12 ++ .../workflow/models/WorkflowCredentialsIn.ts | 4 - .../workflow/models/WorkflowCredentialsOut.ts | 12 ++ src/client/workflow/models/WorkflowIn.ts | 4 - .../services/WorkflowCredentialsService.ts | 44 +++++-- .../workflow/services/WorkflowService.ts | 7 +- .../services/WorkflowVersionService.ts | 7 +- .../workflows/modals/CreateWorkflowModal.vue | 1 - .../modals/UpdateWorkflowCredentialsModal.vue | 1 - .../workflows/modals/UpdateWorkflowModal.vue | 122 +++++++++--------- .../modals/UpdateWorkflowVersionIconModal.vue | 4 +- src/views/workflows/MyWorkflowsView.vue | 1 - 14 files changed, 131 insertions(+), 94 deletions(-) create mode 100644 src/client/workflow/models/IconUpdateOut.ts create mode 100644 src/client/workflow/models/WorkflowCredentialsOut.ts diff --git a/src/client/workflow/index.ts b/src/client/workflow/index.ts index 4f6dc72..3b82994 100644 --- a/src/client/workflow/index.ts +++ b/src/client/workflow/index.ts @@ -12,9 +12,11 @@ export type { DevWorkflowExecutionIn } from './models/DevWorkflowExecutionIn'; export { DocumentationEnum } from './models/DocumentationEnum'; export type { ErrorDetail } from './models/ErrorDetail'; export type { HTTPValidationError } from './models/HTTPValidationError'; +export type { IconUpdateOut } from './models/IconUpdateOut'; export { Status } from './models/Status'; export type { ValidationError } from './models/ValidationError'; export type { WorkflowCredentialsIn } from './models/WorkflowCredentialsIn'; +export type { WorkflowCredentialsOut } from './models/WorkflowCredentialsOut'; export type { WorkflowExecutionIn } from './models/WorkflowExecutionIn'; export type { WorkflowExecutionOut } from './models/WorkflowExecutionOut'; export { WorkflowExecutionStatus } from './models/WorkflowExecutionStatus'; diff --git a/src/client/workflow/models/DevWorkflowExecutionIn.ts b/src/client/workflow/models/DevWorkflowExecutionIn.ts index f985ba9..7e69f9c 100644 --- a/src/client/workflow/models/DevWorkflowExecutionIn.ts +++ b/src/client/workflow/models/DevWorkflowExecutionIn.ts @@ -26,10 +26,6 @@ export type DevWorkflowExecutionIn = { * Token to access the content git repository */ token?: (string | null); - /** - * Username belonging to the token. If not provided, it will be parsed from the git repository URL - */ - username?: (string | null); /** * Mode of the workflow with an alternative entrypoint */ diff --git a/src/client/workflow/models/IconUpdateOut.ts b/src/client/workflow/models/IconUpdateOut.ts new file mode 100644 index 0000000..0499796 --- /dev/null +++ b/src/client/workflow/models/IconUpdateOut.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type IconUpdateOut = { + /** + * URL to the uploaded icon + */ + icon_url: string; +}; + diff --git a/src/client/workflow/models/WorkflowCredentialsIn.ts b/src/client/workflow/models/WorkflowCredentialsIn.ts index 277e7cd..37723a3 100644 --- a/src/client/workflow/models/WorkflowCredentialsIn.ts +++ b/src/client/workflow/models/WorkflowCredentialsIn.ts @@ -8,9 +8,5 @@ export type WorkflowCredentialsIn = { * Token to access the content git repository */ token: string; - /** - * Username belonging to the token. If not provided, it will be parsed from the git repository URL - */ - username?: (string | null); }; diff --git a/src/client/workflow/models/WorkflowCredentialsOut.ts b/src/client/workflow/models/WorkflowCredentialsOut.ts new file mode 100644 index 0000000..3b669c2 --- /dev/null +++ b/src/client/workflow/models/WorkflowCredentialsOut.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type WorkflowCredentialsOut = { + /** + * Token to access the content git repository + */ + token: (string | null); +}; + diff --git a/src/client/workflow/models/WorkflowIn.ts b/src/client/workflow/models/WorkflowIn.ts index 65baab9..544b0e3 100644 --- a/src/client/workflow/models/WorkflowIn.ts +++ b/src/client/workflow/models/WorkflowIn.ts @@ -30,10 +30,6 @@ export type WorkflowIn = { * Token to access the content git repository */ token?: (string | null); - /** - * Username belonging to the token. If not provided, it will be parsed from the git repository URL - */ - username?: (string | null); /** * List of modes with alternative entrypoint the new workflow has */ diff --git a/src/client/workflow/services/WorkflowCredentialsService.ts b/src/client/workflow/services/WorkflowCredentialsService.ts index b1ff8ce..1f9f891 100644 --- a/src/client/workflow/services/WorkflowCredentialsService.ts +++ b/src/client/workflow/services/WorkflowCredentialsService.ts @@ -3,6 +3,7 @@ /* tslint:disable */ /* eslint-disable */ import type { WorkflowCredentialsIn } from '../models/WorkflowCredentialsIn'; +import type { WorkflowCredentialsOut } from '../models/WorkflowCredentialsOut'; import type { CancelablePromise } from '../core/CancelablePromise'; import { OpenAPI } from '../core/OpenAPI'; @@ -11,19 +12,19 @@ import { request as __request } from '../core/request'; export class WorkflowCredentialsService { /** - * Delete the credentials of a workflow - * Delete the credentials for the repository of a workflow. + * Get the credentials of a workflow + * Get the credentials for the repository of a workflow. Only the developer of a workflow can do this. * - * Permission "workflow:delete" required. + * Permission "workflow:update" required. * @param wid ID of a workflow - * @returns void + * @returns WorkflowCredentialsOut Successful Response * @throws ApiError */ - public static workflowCredentialsDeleteWorkflowCredentials( + public static workflowCredentialsGetWorkflowCredentials( wid: string, - ): CancelablePromise<void> { + ): CancelablePromise<WorkflowCredentialsOut> { return __request(OpenAPI, { - method: 'DELETE', + method: 'GET', url: '/workflows/{wid}/credentials', path: { 'wid': wid, @@ -38,7 +39,7 @@ export class WorkflowCredentialsService { } /** - * Delete the credentials of a workflow + * Update the credentials of a workflow * Update the credentials for the repository of a workflow. * * Permission "workflow:update" required. @@ -68,4 +69,31 @@ export class WorkflowCredentialsService { }); } + /** + * Delete the credentials of a workflow + * Delete the credentials for the repository of a workflow. + * + * Permission "workflow:delete" required. + * @param wid ID of a workflow + * @returns void + * @throws ApiError + */ + public static workflowCredentialsDeleteWorkflowCredentials( + wid: string, + ): CancelablePromise<void> { + return __request(OpenAPI, { + method: 'DELETE', + url: '/workflows/{wid}/credentials', + path: { + 'wid': wid, + }, + errors: { + 400: `Error decoding JWT Token`, + 403: `Not authenticated`, + 404: `Entity not Found`, + 422: `Validation Error`, + }, + }); + } + } diff --git a/src/client/workflow/services/WorkflowService.ts b/src/client/workflow/services/WorkflowService.ts index bc9ae91..dad2fcb 100644 --- a/src/client/workflow/services/WorkflowService.ts +++ b/src/client/workflow/services/WorkflowService.ts @@ -4,6 +4,7 @@ /* eslint-disable */ import type { Body_Workflow_Version_upload_workflow_version_icon } from '../models/Body_Workflow_Version_upload_workflow_version_icon'; import type { DocumentationEnum } from '../models/DocumentationEnum'; +import type { IconUpdateOut } from '../models/IconUpdateOut'; import type { Status } from '../models/Status'; import type { WorkflowIn } from '../models/WorkflowIn'; import type { WorkflowOut } from '../models/WorkflowOut'; @@ -365,20 +366,20 @@ export class WorkflowService { /** * Upload icon for workflow version - * Upload an icon for the workflow version. + * Upload an icon for the workflow version and returns the new icon URL. * * Permission "workflow:update" required. * @param wid ID of a workflow * @param gitCommitHash Git commit git_commit_hash of specific version. * @param formData - * @returns string Successful Response + * @returns IconUpdateOut Successful Response * @throws ApiError */ public static workflowVersionUploadWorkflowVersionIcon( wid: string, gitCommitHash: string, formData: Body_Workflow_Version_upload_workflow_version_icon, - ): CancelablePromise<string> { + ): CancelablePromise<IconUpdateOut> { return __request(OpenAPI, { method: 'POST', url: '/workflows/{wid}/versions/{git_commit_hash}/icon', diff --git a/src/client/workflow/services/WorkflowVersionService.ts b/src/client/workflow/services/WorkflowVersionService.ts index 945bd0d..0d46dd9 100644 --- a/src/client/workflow/services/WorkflowVersionService.ts +++ b/src/client/workflow/services/WorkflowVersionService.ts @@ -4,6 +4,7 @@ /* eslint-disable */ import type { Body_Workflow_Version_upload_workflow_version_icon } from '../models/Body_Workflow_Version_upload_workflow_version_icon'; import type { DocumentationEnum } from '../models/DocumentationEnum'; +import type { IconUpdateOut } from '../models/IconUpdateOut'; import type { Status } from '../models/Status'; import type { WorkflowVersion } from '../models/WorkflowVersion'; import type { WorkflowVersionStatus } from '../models/WorkflowVersionStatus'; @@ -183,20 +184,20 @@ export class WorkflowVersionService { /** * Upload icon for workflow version - * Upload an icon for the workflow version. + * Upload an icon for the workflow version and returns the new icon URL. * * Permission "workflow:update" required. * @param wid ID of a workflow * @param gitCommitHash Git commit git_commit_hash of specific version. * @param formData - * @returns string Successful Response + * @returns IconUpdateOut Successful Response * @throws ApiError */ public static workflowVersionUploadWorkflowVersionIcon( wid: string, gitCommitHash: string, formData: Body_Workflow_Version_upload_workflow_version_icon, - ): CancelablePromise<string> { + ): CancelablePromise<IconUpdateOut> { return __request(OpenAPI, { method: 'POST', url: '/workflows/{wid}/versions/{git_commit_hash}/icon', diff --git a/src/components/workflows/modals/CreateWorkflowModal.vue b/src/components/workflows/modals/CreateWorkflowModal.vue index 7759c91..3971a16 100644 --- a/src/components/workflows/modals/CreateWorkflowModal.vue +++ b/src/components/workflows/modals/CreateWorkflowModal.vue @@ -54,7 +54,6 @@ const workflow = reactive<WorkflowIn>({ git_commit_hash: "", initial_version: undefined, token: undefined, - username: undefined, modes: [], }); diff --git a/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue b/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue index 8905188..df4ddd1 100644 --- a/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue +++ b/src/components/workflows/modals/UpdateWorkflowCredentialsModal.vue @@ -33,7 +33,6 @@ const props = defineProps<{ // ============================================================================= const credentials = reactive<WorkflowCredentialsIn>({ token: "", - username: undefined, }); const formState = reactive<{ diff --git a/src/components/workflows/modals/UpdateWorkflowModal.vue b/src/components/workflows/modals/UpdateWorkflowModal.vue index f3e6720..4ce43dc 100644 --- a/src/components/workflows/modals/UpdateWorkflowModal.vue +++ b/src/components/workflows/modals/UpdateWorkflowModal.vue @@ -9,7 +9,7 @@ import type { } from "@/client/workflow"; import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; -import { WorkflowService } from "@/client/workflow"; +import { WorkflowService, WorkflowCredentialsService } from "@/client/workflow"; import { GitRepository, requiredRepositoryFiles, @@ -59,18 +59,39 @@ const formState = reactive<{ loading: boolean; checkRepoLoading: boolean; allowUpload: boolean; + loadCredentials: boolean; + workflowToken?: string; }>({ loading: false, checkRepoLoading: false, allowUpload: false, validated: false, missingFiles: [], + loadCredentials: false, + workflowToken: undefined, }); watch( () => props.workflow, () => { resetForm(); + if (props.workflow.private) { + formState.loadCredentials = true; + WorkflowCredentialsService.workflowCredentialsGetWorkflowCredentials( + props.workflow.workflow_id, + ) + .then((credentials) => { + formState.workflowToken = credentials.token ?? undefined; + }) + .catch(() => { + formState.workflowToken = undefined; + }) + .finally(() => { + formState.loadCredentials = false; + }); + } else { + formState.workflowToken = undefined; + } }, ); @@ -96,21 +117,6 @@ const emit = defineEmits<{ // Functions // ============================================================================= -/* -function iconChanged() { - workflowUpdate.icon = workflowIconInputElement.value?.files?.[0].slice(); - if (workflowUpdate.icon != undefined) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - workflowIconElement.value!.src = URL.createObjectURL( - workflowUpdate.icon.slice(), - ); - } else { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - workflowIconElement.value!.src = latestVersion.value.icon_url ?? ""; - } -} -*/ - function modalClosed() { formState.validated = false; formState.missingFiles = []; @@ -134,12 +140,13 @@ function checkVersionValidity() { function checkRepository() { formState.validated = true; + workflowGitCommitHashElement.value?.setCustomValidity(""); if (workflowUpdateForm.value?.checkValidity() && !formState.allowUpload) { formState.missingFiles = []; - workflowGitCommitHashElement.value?.setCustomValidity(""); const repo = GitRepository.buildRepository( props.workflow.repository_url, workflowUpdate.git_commit_hash, + formState.workflowToken, ); repo .checkFilesExist(requiredRepositoryFiles, true) @@ -250,6 +257,11 @@ onMounted(() => { target="_blank" >{{ props.workflow.repository_url }}</a > + <font-awesome-icon + v-if="props.workflow.private" + class="ms-2" + icon="fa-solid fa-lock" + /> <img :src="latestVersion.icon_url ?? undefined" ref="workflowIconElement" @@ -257,37 +269,37 @@ onMounted(() => { :hidden="!showIcon" /> </div> - <div class="mb-3"> - <label for="workflowGitCommitInput" class="form-label" - >Git Commit Hash</label - > - <div class="input-group"> - <div class="input-group-text"> - <font-awesome-icon icon="fa-solid fa-code-commit" /> + <div class="row"> + <div class="col-8"> + <label for="workflowGitCommitInput" class="form-label" + >Git Commit Hash</label + > + <div class="input-group"> + <div class="input-group-text"> + <font-awesome-icon icon="fa-solid fa-code-commit" /> + </div> + <input + type="text" + class="form-control text-lowercase" + id="workflowGitCommitInput" + placeholder="ba8bcd9..." + required + ref="workflowGitCommitHashElement" + maxlength="40" + pattern="[0-9a-f]{40}" + v-model="workflowUpdate.git_commit_hash" + @change="formState.allowUpload = false" + /> + </div> + <div v-if="formState.missingFiles.length > 0" class="text-danger"> + The following files are missing in the repository + <ul> + <li v-for="file in formState.missingFiles" :key="file"> + {{ file }} + </li> + </ul> </div> - <input - type="text" - class="form-control text-lowercase" - id="workflowGitCommitInput" - placeholder="ba8bcd9..." - required - ref="workflowGitCommitHashElement" - maxlength="40" - pattern="[0-9a-f]{40}" - v-model="workflowUpdate.git_commit_hash" - @change="formState.allowUpload = false" - /> </div> - </div> - <div v-if="formState.missingFiles.length > 0" class="text-danger"> - The following files are missing in the repository - <ul> - <li v-for="file in formState.missingFiles" :key="file"> - {{ file }} - </li> - </ul> - </div> - <div class="row mb-3"> <div class="col-4"> <label for="workflowVersionInput" class="form-label">Version</label> <div class="input-group"> @@ -311,22 +323,6 @@ onMounted(() => { Previous Version: {{ latestVersion.version }} </div> </div> - <div class="col-8"> - <label for="workflowIconInput" class="form-label" - >Optional Icon</label - > - <input - type="file" - ref="workflowIconInputElement" - accept="image/*" - class="form-control" - id="workflowIconInput" - aria-describedby="iconHelp" - /> - <div id="iconHelp" class="form-text"> - If not set, the previous icon will be used - </div> - </div> </div> </form> </template> @@ -335,7 +331,7 @@ onMounted(() => { type="button" class="btn btn-info me-auto" @click="checkRepository" - :disabled="formState.allowUpload" + :disabled="formState.allowUpload || formState.loadCredentials" > <span v-if="formState.checkRepoLoading" diff --git a/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue b/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue index dafb7ff..ede37a2 100644 --- a/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue +++ b/src/components/workflows/modals/UpdateWorkflowVersionIconModal.vue @@ -98,9 +98,9 @@ function updateIcon() { icon: iconUpdate.icon, }, ) - .then((icon_url) => { + .then((response) => { formState.uploadIcon = true; - emit("icon-updated", props.version, icon_url); + emit("icon-updated", props.version, response.icon_url); successToast?.show(); updateIconModal?.hide(); }) diff --git a/src/views/workflows/MyWorkflowsView.vue b/src/views/workflows/MyWorkflowsView.vue index 7a05c2e..882df78 100644 --- a/src/views/workflows/MyWorkflowsView.vue +++ b/src/views/workflows/MyWorkflowsView.vue @@ -100,7 +100,6 @@ function confirmedWorkflowVersionIconUpdate( version: WorkflowVersion, icon_url?: string, ) { - console.log("New Url", icon_url); const wIndex = workflowsState.workflows.findIndex( (w) => w.workflow_id == version.workflow_id, ); -- GitLab