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

Merge 24.1 #18386

Merged
merged 19 commits into from
Jun 13, 2024
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
5 changes: 5 additions & 0 deletions client/src/api/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8394,6 +8394,11 @@ export interface components {
* @default false
*/
use_cached_job?: boolean | null;
/**
* Version
* @description The version of the workflow to invoke.
*/
version?: number | null;
};
/**
* ItemTagsCreatePayload
Expand Down
4 changes: 3 additions & 1 deletion client/src/components/Workflow/Editor/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,9 @@ export default {
this.report.markdown = markdown;
},
onRun() {
const runUrl = `/workflows/run?id=${this.id}`;
const runUrl = `/workflows/run?id=${this.id}${
this.version !== undefined ? `&version=${this.version}` : ""
}`;
this.onNavigate(runUrl);
},
async onNavigate(url, forceSave = false, ignoreChanges = false) {
Expand Down
10 changes: 7 additions & 3 deletions client/src/components/Workflow/Run/WorkflowRun.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ const router = useRouter();

interface Props {
workflowId: string;
version?: string;
preferSimpleForm?: boolean;
simpleFormTargetHistory?: string;
simpleFormUseJobCache?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
version: undefined,
preferSimpleForm: false,
simpleFormTargetHistory: "current",
simpleFormUseJobCache: false,
Expand All @@ -49,7 +51,9 @@ const workflowName = ref("");
const workflowModel: any = ref(null);

const currentHistoryId = computed(() => historyStore.currentHistoryId);
const editorLink = computed(() => `/workflows/edit?id=${props.workflowId}`);
const editorLink = computed(
() => `/workflows/edit?id=${props.workflowId}${props.version ? `&version=${props.version}` : ""}`
);
const historyStatusKey = computed(() => `${currentHistoryId.value}_${lastUpdateTime.value}`);
const isOwner = computed(() => currentUser.value?.username === workflowModel.value.runData.owner);
const lastUpdateTime = computed(() => historyItemsStore.lastUpdateTime);
Expand All @@ -74,7 +78,7 @@ function handleSubmissionError(error: string) {
}

function loadRun() {
getRunData(props.workflowId)
getRunData(props.workflowId, props.version || undefined)
.then((runData) => {
const incomingModel = new WorkflowRunModel(runData);
simpleForm.value = props.preferSimpleForm;
Expand Down Expand Up @@ -116,7 +120,7 @@ function loadRun() {
}

async function onImport() {
const response = await copyWorkflow(props.workflowId, workflowModel.value.runData.owner);
const response = await copyWorkflow(props.workflowId, workflowModel.value.runData.owner, props.version);
router.push(`/workflows/edit?id=${response.id}`);
}

Expand Down
3 changes: 2 additions & 1 deletion client/src/components/Workflow/Run/WorkflowRunForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</span>
</BAlert>
<div class="h4 clearfix mb-3">
<b>Workflow: {{ model.name }}</b>
<b>Workflow: {{ model.name }}</b> <i>(version: {{ model.runData.version + 1 }})</i>
<ButtonSpinner
id="run-workflow"
class="float-right"
Expand Down Expand Up @@ -231,6 +231,7 @@ export default {
// the user is already warned if tool versions are wrong,
// they can still choose to invoke the workflow anyway.
require_exact_tool_versions: false,
version: this.model.runData.version,
};

console.debug("WorkflowRunForm::onExecute()", "Ready for submission.", jobDef);
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/Workflow/Run/WorkflowRunFormSimple.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
or send the results to a new one using the run settings ⚙️
</span>
</BAlert>
<b>Workflow: {{ model.name }}</b>
<b>Workflow: {{ model.name }}</b> <i>(version: {{ model.runData.version + 1 }})</i>
<ButtonSpinner
id="run-workflow"
:wait="waitingForRequest"
Expand Down Expand Up @@ -200,6 +200,7 @@ export default {
batch: true,
use_cached_job: this.useCachedJobs,
require_exact_tool_versions: false,
version: this.model.runData.version,
};
if (this.sendToNewHistory) {
data.new_history_name = this.model.name;
Expand Down
8 changes: 6 additions & 2 deletions client/src/components/Workflow/Run/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ import { rethrowSimple } from "utils/simple-error";
* for implementation). This contains the data needed to render the UI for workflows.
*
* @param {String} workflowId - (Stored?) Workflow ID to fetch data for.
* @param {String} version - Version of the workflow to fetch.
*/
export async function getRunData(workflowId) {
const url = `${getAppRoot()}api/workflows/${workflowId}/download?style=run`;
export async function getRunData(workflowId, version = null) {
let url = `${getAppRoot()}api/workflows/${workflowId}/download?style=run`;
if (version) {
url += `&version=${version}`;
}
try {
const response = await axios.get(url);
return response.data;
Expand Down
10 changes: 8 additions & 2 deletions client/src/components/Workflow/WorkflowRunButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { library } from "@fortawesome/fontawesome-svg-core";
import { faPlay } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BButton } from "bootstrap-vue";
import { computed } from "vue";

library.add(faPlay);

Expand All @@ -11,9 +12,14 @@ interface Props {
full?: boolean;
title?: string;
disabled?: boolean;
version?: number;
}

defineProps<Props>();
const props = defineProps<Props>();

const runPath = computed(
() => `/workflows/run?id=${props.id}${props.version !== undefined ? `&version=${props.version}` : ""}`
);
</script>

<template>
Expand All @@ -25,7 +31,7 @@ defineProps<Props>();
variant="primary"
size="sm"
:disabled="disabled"
:to="`/workflows/run?id=${id}`">
:to="runPath">
<FontAwesomeIcon :icon="faPlay" fixed-width />

<span v-if="full" v-localize>Run</span>
Expand Down
8 changes: 6 additions & 2 deletions client/src/components/Workflow/workflows.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@ export async function updateWorkflow(id: string, changes: object): Promise<Workf
return data;
}

export async function copyWorkflow(id: string, currentOwner: string): Promise<Workflow> {
const { data: workflowData } = await axios.get(withPrefix(`/api/workflows/${id}/download`));
export async function copyWorkflow(id: string, currentOwner: string, version?: string): Promise<Workflow> {
let path = `/api/workflows/${id}/download`;
if (version) {
path += `?version=${version}`;
}
const { data: workflowData } = await axios.get(withPrefix(path));

workflowData.name = `Copy of ${workflowData.name}`;
const currentUsername = useUserStore().currentUser?.username;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ function getWorkflowName() {
size="sm"
variant="secondary"
:disabled="isDeletedWorkflow"
:to="`/workflows/edit?id=${getWorkflowId()}`">
:to="`/workflows/edit?id=${getWorkflowId()}&version=${workflowVersion}`">
<FontAwesomeIcon :icon="faEdit" />
<span v-localize>Edit</span>
</BButton>
Expand All @@ -218,7 +218,8 @@ function getWorkflowName() {
: 'This workflow has been deleted.'
"
:disabled="isDeletedWorkflow"
full />
full
:version="workflowVersion" />
</BButtonGroup>
</div>
</div>
Expand Down
2 changes: 2 additions & 0 deletions client/src/entry/analysis/modules/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export default {
},
workflowParams() {
const workflowId = this.query.workflow_id;
const version = this.query.version;
let preferSimpleForm = this.config.simplified_workflow_run_ui == "prefer";
const preferSimpleFormOverride = this.query.simplified_workflow_run_ui;
if (preferSimpleFormOverride == "prefer") {
Expand All @@ -68,6 +69,7 @@ export default {
const simpleFormUseJobCache = this.config.simplified_workflow_run_ui_job_cache == "on";
return {
workflowId,
version,
preferSimpleForm,
simpleFormTargetHistory,
simpleFormUseJobCache,
Expand Down
5 changes: 5 additions & 0 deletions client/src/entry/analysis/modules/WorkflowEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default {
return {
storedWorkflowId: null,
workflowId: null,
version: null,
editorConfig: null,
editorReloadKey: 0,
};
Expand All @@ -46,6 +47,7 @@ export default {

this.storedWorkflowId = Query.get("id");
this.workflowId = Query.get("workflow_id");
this.version = Query.get("version");

const params = {};

Expand All @@ -54,6 +56,9 @@ export default {
} else if (this.storedWorkflowId) {
params.id = this.storedWorkflowId;
}
if (this.version) {
params.version = this.version;
}

this.editorConfig = await urlData({ url: "/workflow/editor", params });

Expand Down
5 changes: 4 additions & 1 deletion client/src/entry/analysis/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,10 @@ export function getRouter(Galaxy) {
redirect: redirectAnon(),
props: (route) => ({
config: Galaxy.config,
query: { workflow_id: route.query.id },
query: {
workflow_id: route.query.id,
version: route.query.version,
},
}),
},
{
Expand Down
7 changes: 6 additions & 1 deletion lib/galaxy/jobs/runners/pulsar.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,12 @@ def check_watched_item_state(self, job_state):

def _update_job_state_for_status(self, job_state, pulsar_status, full_status=None):
log.debug("(%s) Received status update: %s", job_state.job_id, pulsar_status)
if pulsar_status in ["complete", "cancelled"] or job_state.job_wrapper.get_state() == model.Job.states.STOPPED:
if pulsar_status in ["complete", "cancelled"]:
self.mark_as_finished(job_state)
return None
if job_state.job_wrapper.get_state() == model.Job.states.STOPPED:
client = self.get_client_from_state(job_state)
client.kill()
self.mark_as_finished(job_state)
return None
if pulsar_status in ["failed", "lost"]:
Expand Down
44 changes: 30 additions & 14 deletions lib/galaxy/managers/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def purge(self, user, flush=True):
private_role = self.app.security_agent.get_private_user_role(user)
if private_role is None:
raise exceptions.InconsistentDatabase(
"User '%s' private role is missing while attempting to purge deleted user." % user.email
f"User {user.email} private role is missing while attempting to purge deleted user."
)
# Delete History
for active_history in user.active_histories:
Expand Down Expand Up @@ -667,23 +667,11 @@ def get_or_create_remote_user(self, remote_user_email):
self.app.security_agent.user_set_default_permissions(user)
self.app.security_agent.user_set_default_permissions(user, history=True, dataset=True)
elif user is None:
username = remote_user_email.split("@", 1)[0].lower()
random.seed()
user = self.app.model.User(email=remote_user_email)
user.set_random_password(length=12)
user.external = True
# Replace invalid characters in the username
for char in [x for x in username if x not in f"{string.ascii_lowercase + string.digits}-."]:
username = username.replace(char, "-")
# Find a unique username - user can change it later
stmt = select(self.app.model.User).filter_by(username=username).limit(1)
if self.session().scalars(stmt).first():
i = 1
stmt = select(self.app.model.User).filter_by(username=f"{username}-{str(i)}").limit(1)
while self.session().scalars(stmt).first():
i += 1
username += f"-{str(i)}"
user.username = username
user.username = username_from_email(self.session(), remote_user_email, self.app.model.User)
self.session().add(user)
with transaction(self.session()):
self.session().commit()
Expand Down Expand Up @@ -877,3 +865,31 @@ def _add_parsers(self):
)

self.fn_filter_parsers.update({})


def username_from_email(session, email, model_class=User):
"""Get next available username generated based on email"""
username = email.split("@", 1)[0].lower()
username = filter_out_invalid_username_characters(username)
if username_exists(session, username, model_class):
username = generate_next_available_username(session, username, model_class)
return username


def filter_out_invalid_username_characters(username):
"""Replace invalid characters in username"""
for char in [x for x in username if x not in f"{string.ascii_lowercase + string.digits}-."]:
username = username.replace(char, "-")
return username


def username_exists(session, username: str, model_class=User):
return bool(get_user_by_username(session, username, model_class))


def generate_next_available_username(session, username, model_class=User):
"""Generate unique username; user can change it later"""
i = 1
while session.execute(select(model_class).where(model_class.username == f"{username}-{i}")).first():
i += 1
return f"{username}-{i}"
3 changes: 3 additions & 0 deletions lib/galaxy/managers/workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,9 @@ def to_format_2(wf_dict, **kwds):
raise exceptions.RequestParameterInvalidException(f"Unknown workflow style {style}")
if version is not None:
wf_dict["version"] = version
# If returning a run-form workflow for a specific version, use that version's name
if style == "run":
wf_dict["name"] = workflow.name
else:
wf_dict["version"] = len(stored.workflows) - 1
return wf_dict
Expand Down
Loading
Loading