Skip to content

Commit

Permalink
Merge branch 'release_24.1' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
mvdbeek committed Jul 25, 2024
2 parents e18ba47 + 9c507f2 commit 423c371
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 53 deletions.
17 changes: 11 additions & 6 deletions client/src/components/User/APIKey/APIKeyItem.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
<script setup>
import { library } from "@fortawesome/fontawesome-svg-core";
import { faEye, faEyeSlash, faKey, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { getGalaxyInstance } from "app";
import CopyToClipboard from "components/CopyToClipboard";
import UtcDate from "components/UtcDate";
import { ref } from "vue";
import svc from "./model/service";
library.add(faEye, faEyeSlash, faKey, faTrash);
defineProps({
item: {
type: Object,
Expand Down Expand Up @@ -35,12 +40,7 @@ const deleteKey = () => {
<b-card title="Current API key">
<div class="d-flex justify-content-between w-100">
<div class="w-100">
<b-input-group
class="w-100"
@blur="hover = false"
@focus="hover = true"
@mouseover="hover = true"
@mouseleave="hover = false">
<b-input-group class="w-100">
<b-input-group-prepend>
<b-input-group-text>
<icon icon="key" />
Expand All @@ -57,6 +57,11 @@ const deleteKey = () => {
<b-input-group-text>
<CopyToClipboard message="Key was copied to clipboard" :text="item.key" title="Copy key" />
</b-input-group-text>

<b-button v-b-tooltip.hover title="Show/hide key" @click="hover = !hover">
<FontAwesomeIcon :icon="hover ? faEyeSlash : faEye" />
</b-button>

<b-button title="Delete api key" @click="toggleDeleteModal">
<icon icon="trash" />
</b-button>
Expand Down
93 changes: 52 additions & 41 deletions client/src/components/Workflow/Run/WorkflowRun.vue
Original file line number Diff line number Diff line change
Expand Up @@ -77,55 +77,66 @@ function handleSubmissionError(error: string) {
submissionError.value = errorMessageAsString(error);
}
function loadRun() {
getRunData(props.workflowId, props.version || undefined)
.then((runData) => {
const incomingModel = new WorkflowRunModel(runData);
simpleForm.value = props.preferSimpleForm;
if (simpleForm.value) {
// These only work with PJA - the API doesn't evaluate them at
// all outside that context currently. The main workflow form renders
// these dynamically and takes care of all the validation and setup details
// on the frontend. If these are implemented on the backend at some
// point this restriction can be lifted.
if (incomingModel.hasReplacementParametersInToolForm) {
console.log("cannot render simple workflow form - has ${} values in tool steps");
simpleForm.value = false;
}
// If there are required parameters in a tool form (a disconnected runtime
// input), we have to render the tool form steps and cannot use the
// simplified tool form.
if (incomingModel.hasOpenToolSteps) {
console.log(
"cannot render simple workflow form - one or more tools have disconnected runtime inputs"
);
simpleForm.value = false;
}
// Just render the whole form for resource request parameters (kind of
// niche - I'm not sure anyone is using these currently anyway).
if (incomingModel.hasWorkflowResourceParameters) {
console.log(`Cannot render simple workflow form - workflow resource parameters are configured`);
simpleForm.value = false;
}
async function loadRun() {
try {
const runData = await getRunData(props.workflowId, props.version || undefined);
const incomingModel = new WorkflowRunModel(runData);
simpleForm.value = props.preferSimpleForm;
if (simpleForm.value) {
// These only work with PJA - the API doesn't evaluate them at
// all outside that context currently. The main workflow form renders
// these dynamically and takes care of all the validation and setup details
// on the frontend. If these are implemented on the backend at some
// point this restriction can be lifted.
if (incomingModel.hasReplacementParametersInToolForm) {
console.log("cannot render simple workflow form - has ${} values in tool steps");
simpleForm.value = false;
}
hasUpgradeMessages.value = incomingModel.hasUpgradeMessages;
hasStepVersionChanges.value = incomingModel.hasStepVersionChanges;
workflowName.value = incomingModel.name;
workflowModel.value = incomingModel;
loading.value = false;
})
.catch((response) => {
workflowError.value = errorMessageAsString(response);
});
// If there are required parameters in a tool form (a disconnected runtime
// input), we have to render the tool form steps and cannot use the
// simplified tool form.
if (incomingModel.hasOpenToolSteps) {
console.log("cannot render simple workflow form - one or more tools have disconnected runtime inputs");
simpleForm.value = false;
}
// Just render the whole form for resource request parameters (kind of
// niche - I'm not sure anyone is using these currently anyway).
if (incomingModel.hasWorkflowResourceParameters) {
console.log(`Cannot render simple workflow form - workflow resource parameters are configured`);
simpleForm.value = false;
}
}
hasUpgradeMessages.value = incomingModel.hasUpgradeMessages;
hasStepVersionChanges.value = incomingModel.hasStepVersionChanges;
workflowName.value = incomingModel.name;
workflowModel.value = incomingModel;
loading.value = false;
} catch (e) {
workflowError.value = errorMessageAsString(e);
}
}
async function onImport() {
const response = await copyWorkflow(props.workflowId, workflowModel.value.runData.owner, props.version);
router.push(`/workflows/edit?id=${response.id}`);
}
const advancedForm = ref(false);
const fromVariant = computed<"simple" | "advanced">(() => {
if (advancedForm.value) {
return "advanced";
} else if (simpleForm.value) {
return "simple";
} else {
return "advanced";
}
});
function showAdvanced() {
simpleForm.value = false;
advancedForm.value = true;
}
onMounted(() => {
Expand Down Expand Up @@ -185,7 +196,7 @@ defineExpose({
Workflow submission failed: {{ submissionError }}
</BAlert>
<WorkflowRunFormSimple
v-else-if="simpleForm"
v-else-if="fromVariant === 'simple'"
:model="workflowModel"
:target-history="simpleFormTargetHistory"
:use-job-cache="simpleFormUseJobCache"
Expand Down
3 changes: 1 addition & 2 deletions lib/galaxy/datatypes/tabular.py
Original file line number Diff line number Diff line change
Expand Up @@ -1815,15 +1815,14 @@ def set_meta(
with open(dataset.get_file_name()) as dataset_fh:
comment_lines = 0
column_headers = None
cleaned_column_types = None
cleaned_column_types = []
number_of_columns = 0
for i, line in enumerate(dataset_fh):
line = line.strip("\n")
if line.startswith("#"):
if line.startswith("#h"):
column_headers = line.split("\t")[1:]
elif line.startswith("#f"):
cleaned_column_types = []
for column_type in line.split("\t")[1:]:
if column_type == "Hex":
cleaned_column_types.append("str")
Expand Down
2 changes: 1 addition & 1 deletion lib/galaxy/tools/parameters/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -1511,7 +1511,7 @@ def get_options(self, trans, other_values):
except Exception:
column_list = self.get_column_list(trans, other_values)
if self.numerical: # If numerical was requested, filter columns based on metadata
if hasattr(dataset, "metadata") and hasattr(dataset.metadata, "column_types"):
if hasattr(dataset, "metadata") and getattr(dataset.metadata, "column_types", None) is not None:
if len(dataset.metadata.column_types) >= len(column_list):
numerics = [i for i, x in enumerate(dataset.metadata.column_types) if x in ["int", "float"]]
column_list = [column_list[i] for i in numerics]
Expand Down
3 changes: 2 additions & 1 deletion lib/galaxy/util/zipstream.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ def response(self) -> Iterator[bytes]:
def get_headers(self) -> Dict[str, str]:
headers = {}
if self.archive_name:
headers["Content-Disposition"] = f'attachment; filename="{self.archive_name}.zip"'
archive_name = self.archive_name.encode("latin-1", "replace").decode("latin-1")
headers["Content-Disposition"] = f'attachment; filename="{archive_name}.zip"'
if self.upstream_mod_zip:
headers["X-Archive-Files"] = "zip"
else:
Expand Down
7 changes: 6 additions & 1 deletion lib/galaxy/web/framework/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,13 @@ def handle_request(self, request_id, path_info, environ, start_response, body_re
raise
trans.controller = controller_name
trans.action = action

# Action can still refer to invalid and/or inaccurate paths here, so we use the actual
# controller and method names to set the timing key.

action_tag = getattr(method, "__name__", "default")
environ["controller_action_key"] = (
f"{'api' if environ['is_api_request'] else 'web'}.{controller_name}.{action or 'default'}"
f"{'api' if environ['is_api_request'] else 'web'}.{controller_name}.{action_tag}"
)
# Combine mapper args and query string / form args and call
kwargs = trans.request.params.mixed()
Expand Down
3 changes: 2 additions & 1 deletion lib/galaxy/webapps/galaxy/services/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,8 @@ def _patch_library_inputs(self, trans: ProvidesHistoryContext, inputs, target_hi
def _patch_library_dataset(self, trans: ProvidesHistoryContext, v, target_history):
if isinstance(v, dict) and "id" in v and v.get("src") == "ldda":
ldda = trans.sa_session.get(LibraryDatasetDatasetAssociation, self.decode_id(v["id"]))
assert ldda
if not ldda:
raise exceptions.ObjectNotFound("Could not find library dataset dataset association.")
if trans.user_is_admin or trans.app.security_agent.can_access_dataset(
trans.get_current_user_roles(), ldda.dataset
):
Expand Down
8 changes: 8 additions & 0 deletions lib/galaxy_test/api/test_dataset_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,14 @@ def test_list_list_list_download(self):
namelist = archive.namelist()
assert len(namelist) == 3, f"Expected 3 elements in [{namelist}]"

def test_download_non_english_characters(self):
with self.dataset_populator.test_history() as history_id:
name = "دیتاست"
payload = self.dataset_collection_populator.create_list_payload(history_id, name=name)
hdca_id = self.dataset_populator.fetch(payload, wait=True).json()["outputs"][0]["id"]
create_response = self._download_dataset_collection(history_id=history_id, hdca_id=hdca_id)
self._assert_status_code_is(create_response, 200)

@requires_new_user
def test_hda_security(self):
with self.dataset_populator.test_history(require_new=False) as history_id:
Expand Down

0 comments on commit 423c371

Please sign in to comment.