Skip to content

Commit

Permalink
Merge branch 'release_23.2' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
mvdbeek committed Feb 8, 2024
2 parents 5f25655 + 327dfa0 commit b278d8a
Show file tree
Hide file tree
Showing 60 changed files with 254 additions and 336 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
case "$TARGET_BRANCH" in
release_[[:digit:]][[:digit:]].[[:digit:]][[:digit:]]|master)
release_[[:digit:]][[:digit:]].[[:digit:]][[:digit:]] | release_[[:digit:]][[:digit:]].[[:digit:]] | master)
UPLOAD_DIR=$TARGET_BRANCH
;;
dev)
Expand Down
55 changes: 55 additions & 0 deletions client/src/components/Form/Elements/FormData/FormData.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,61 @@ describe("FormData", () => {
expect(wrapper.emitted().input[1][0]).toEqual(value_sorted);
});

it("sorts mixed dces and hdas", async () => {
const sortOptions = {
hda: [
{ id: "hda1", hid: 1, name: "hdaName1", src: "hda", tags: ["tag1"] },
{ id: "hda2", hid: 2, name: "hdaName2", src: "hda", tags: ["tag1", "tag2"] },
{ id: "hda3", hid: 3, name: "hdaName3", src: "hda", tags: ["tag2", "tag3"] },
{ id: "hda4", hid: 4, name: "hdaName4", src: "hda" },
],
dce: [
{ id: "dce1", name: "dceName1", src: "dce", is_dataset: true },
{ id: "dce2", name: "dceName2", src: "dce", is_dataset: true },
{ id: "dce3", name: "dceName3", src: "dce", is_dataset: true },
{ id: "dce4", name: "dceName4", src: "dce", is_dataset: true },
],
};
const wrapper = createTarget({
//intermix hdas and dces in the selected options
value: {
values: [
{ id: "hda1", src: "hda" },
{ id: "dce4", src: "dce" },
{ id: "dce2", src: "dce" },
{ id: "hda2", src: "hda" },
{ id: "dce3", src: "dce" },
],
},
multiple: true,
optional: true,
options: sortOptions,
});
const selectedValues = wrapper.findAll(SELECTED_VALUE);
expect(selectedValues.length).toBe(5);
// when dces are mixed in their values are shown first and are
// ordered by id descending
expect(selectedValues.at(0).text()).toBe("dceName4 (as dataset)");
expect(selectedValues.at(1).text()).toBe("dceName3 (as dataset)");
expect(selectedValues.at(2).text()).toBe("dceName2 (as dataset)");
expect(selectedValues.at(3).text()).toBe("2: hdaName2");
expect(selectedValues.at(4).text()).toBe("1: hdaName1");
await selectedValues.at(0).trigger("click");
const value_sorted = {
batch: false,
product: false,
values: [
// when dces are mixed in, they are emitted first
// and are sorted by id descending
{ id: "dce3", map_over_type: null, src: "dce" },
{ id: "dce2", map_over_type: null, src: "dce" },
{ id: "hda1", map_over_type: null, src: "hda" },
{ id: "hda2", map_over_type: null, src: "hda" },
],
};
expect(wrapper.emitted().input[1][0]).toEqual(value_sorted);
});

it("dataset collection as hda", async () => {
const wrapper = createTarget({
value: { values: [{ id: "dce1", src: "dce" }] },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,28 +84,41 @@ describe("History Selection Operations", () => {
expect(wrapper.find(option).exists()).toBe(false);
});

it("should display 'unhide' option only on hidden items", async () => {
it("should display 'unhide' option on hidden items", async () => {
const option = '[data-description="unhide option"]';
expect(wrapper.find(option).exists()).toBe(false);
await wrapper.setProps({ filterText: "visible:false" });
expect(wrapper.find(option).exists()).toBe(true);
});

it("should display 'delete' option only on non-deleted items", async () => {
it("should display 'unhide' option when hidden and visible items are mixed", async () => {
const option = '[data-description="unhide option"]';
expect(wrapper.find(option).exists()).toBe(false);
await wrapper.setProps({ filterText: "visible:any" });
expect(wrapper.find(option).exists()).toBe(true);
});

it("should display 'delete' option on non-deleted items", async () => {
const option = '[data-description="delete option"]';
expect(wrapper.find(option).exists()).toBe(true);
await wrapper.setProps({ filterText: "deleted:true" });
expect(wrapper.find(option).exists()).toBe(false);
});

it("should display 'delete' option when non-deleted and deleted items are mixed", async () => {
const option = '[data-description="delete option"]';
await wrapper.setProps({ filterText: "deleted:any" });
expect(wrapper.find(option).exists()).toBe(true);
});

it("should display 'permanently delete' option always", async () => {
const option = '[data-description="purge option"]';
expect(wrapper.find(option).exists()).toBe(true);
await wrapper.setProps({ filterText: "deleted:true" });
expect(wrapper.find(option).exists()).toBe(true);
});

it("should display 'undelete' option only on deleted and non-purged items", async () => {
it("should display 'undelete' option on deleted and non-purged items", async () => {
const option = '[data-description="undelete option"]';
expect(wrapper.find(option).exists()).toBe(false);
await wrapper.setProps({
Expand All @@ -115,6 +128,15 @@ describe("History Selection Operations", () => {
expect(wrapper.find(option).exists()).toBe(true);
});

it("should display 'undelete' option when non-purged items (deleted or not) are mixed", async () => {
const option = '[data-description="undelete option"]';
await wrapper.setProps({
filterText: "deleted:any",
contentSelection: getNonPurgedContentSelection(),
});
expect(wrapper.find(option).exists()).toBe(true);
});

it("should not display 'undelete' when is manual selection mode and all selected items are purged", async () => {
const option = '[data-description="undelete option"]';
await wrapper.setProps({
Expand All @@ -136,6 +158,17 @@ describe("History Selection Operations", () => {
expect(wrapper.find(option).exists()).toBe(true);
});

it("should display 'undelete' option when is query selection mode and filtering by any deleted state", async () => {
const option = '[data-description="undelete option"]';
// In query selection mode we don't know if some items may not be purged, so we allow to undelete
await wrapper.setProps({
filterText: "deleted:any",
contentSelection: getPurgedContentSelection(),
isQuerySelection: true,
});
expect(wrapper.find(option).exists()).toBe(true);
});

it("should display collection building options only on visible and non-deleted items", async () => {
const buildListOption = '[data-description="build list"]';
const buildPairOption = '[data-description="build pair"]';
Expand All @@ -151,6 +184,14 @@ describe("History Selection Operations", () => {
expect(wrapper.find(buildListOption).exists()).toBe(false);
expect(wrapper.find(buildPairOption).exists()).toBe(false);
expect(wrapper.find(buildListOfPairsOption).exists()).toBe(false);
await wrapper.setProps({ filterText: "visible:any" });
expect(wrapper.find(buildListOption).exists()).toBe(false);
expect(wrapper.find(buildPairOption).exists()).toBe(false);
expect(wrapper.find(buildListOfPairsOption).exists()).toBe(false);
await wrapper.setProps({ filterText: "deleted:any" });
expect(wrapper.find(buildListOption).exists()).toBe(false);
expect(wrapper.find(buildPairOption).exists()).toBe(false);
expect(wrapper.find(buildListOfPairsOption).exists()).toBe(false);
});

it("should display list building option when all are selected", async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
data-description="undelete option">
<span v-localize>Undelete</span>
</b-dropdown-item>
<b-dropdown-item v-if="!showDeleted" v-b-modal:delete-selected-content data-description="delete option">
<b-dropdown-item
v-if="!showStrictDeleted"
v-b-modal:delete-selected-content
data-description="delete option">
<span v-localize>Delete</span>
</b-dropdown-item>
<b-dropdown-item v-b-modal:purge-selected-content data-description="purge option">
Expand Down Expand Up @@ -191,10 +194,14 @@ export default {
computed: {
/** @returns {Boolean} */
showHidden() {
return HistoryFilters.checkFilter(this.filterText, "visible", false);
return !HistoryFilters.checkFilter(this.filterText, "visible", true);
},
/** @returns {Boolean} */
showDeleted() {
return !HistoryFilters.checkFilter(this.filterText, "deleted", false);
},
/** @returns {Boolean} */
showStrictDeleted() {
return HistoryFilters.checkFilter(this.filterText, "deleted", true);
},
/** @returns {Boolean} */
Expand Down
8 changes: 7 additions & 1 deletion client/src/components/Markdown/MarkdownSelector.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<template>
<span>
<b-modal v-model="modalShow" :title="title" ok-title="Continue" @ok="onOk" @cancel="onCancel">
<b-modal
v-model="modalShow"
:title="title"
ok-title="Continue"
@ok="onOk"
@cancel="onCancel"
@hidden="onCancel">
<div class="ml-2">
<h2 class="mb-3 h-text">Select {{ labelTitle }} Label:</h2>
<div v-if="hasLabels">
Expand Down
4 changes: 3 additions & 1 deletion client/src/components/Tool/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { getAppRoot } from "onload/loadConfig";
import { copy } from "utils/clipboard";

export function copyLink(toolId, message) {
copy(`${window.location.origin + getAppRoot()}root?tool_id=${toolId}`, message);
const link = `${window.location.origin + getAppRoot()}root?tool_id=${toolId}`;
// Encode the link to handle special characters in tool id
copy(encodeURI(link), message);
}

export function copyId(toolId, message) {
Expand Down
36 changes: 36 additions & 0 deletions client/src/components/Tool/utilities.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { copyLink } from "./utilities";

const writeText = jest.fn();

Object.assign(navigator, {
clipboard: {
writeText,
},
});

describe("copyLink", () => {
beforeEach(() => {
(navigator.clipboard.writeText as jest.Mock).mockResolvedValue(undefined);
});

it("should copy the link to the clipboard", () => {
const toolId = "MyToolId";
copyLink(toolId);
expect(writeText).toHaveBeenCalledTimes(1);
expect(writeText).toHaveBeenCalledWith(expect.stringContaining(toolId));
});

it("should encode the tool id with spaces", () => {
const toolId = "My Tool Id";
copyLink(toolId);
expect(writeText).toHaveBeenCalledTimes(1);
expect(writeText).toHaveBeenCalledWith(expect.stringContaining("My%20Tool%20Id"));
});

it("should not encode the character '+' in the tool id", () => {
const toolId = "My Tool Id+1";
copyLink(toolId);
expect(writeText).toHaveBeenCalledTimes(1);
expect(writeText).toHaveBeenCalledWith(expect.stringContaining("My%20Tool%20Id+1"));
});
});
44 changes: 24 additions & 20 deletions client/src/components/Upload/DefaultBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,18 +99,22 @@ const listExtensions = computed(() => props.effectiveExtensions.filter((ext) =>
const showHelper = computed(() => Object.keys(uploadItems.value).length === 0);
const uploadValues = computed(() => Object.values(uploadItems.value));
const queue = new UploadQueue({
announce: eventAnnounce,
chunkSize: props.chunkUploadSize,
complete: eventComplete,
error: eventError,
get: (index) => uploadItems.value[index],
historyId: historyId.value,
multiple: props.multiple,
progress: eventProgress,
success: eventSuccess,
warning: eventWarning,
});
const queue = computed(() => createUploadQueue());
function createUploadQueue() {
return new UploadQueue({
announce: eventAnnounce,
chunkSize: props.chunkUploadSize,
complete: eventComplete,
error: eventError,
get: (index) => uploadItems.value[index],
historyId: historyId.value,
multiple: props.multiple,
progress: eventProgress,
success: eventSuccess,
warning: eventWarning,
});
}
/** Add files to queue */
function addFiles(files, immediate = false) {
Expand All @@ -119,9 +123,9 @@ function addFiles(files, immediate = false) {
eventReset();
}
if (props.multiple) {
queue.add(files);
queue.value.add(files);
} else if (files.length > 0) {
queue.add([files[0]]);
queue.value.add([files[0]]);
}
}
}
Expand Down Expand Up @@ -165,7 +169,7 @@ function eventComplete() {
/** Create a new file */
function eventCreate() {
queue.add([{ name: DEFAULT_FILE_NAME, size: 0, mode: "new" }]);
queue.value.add([{ name: DEFAULT_FILE_NAME, size: 0, mode: "new" }]);
}
/** Error */
Expand Down Expand Up @@ -207,14 +211,14 @@ function eventRemove(index) {
counterAnnounce.value--;
}
Vue.delete(uploadItems.value, index);
queue.remove(index);
queue.value.remove(index);
}
/** Show remote files dialog or FTP files */
function eventRemoteFiles() {
filesDialog(
(items) => {
queue.add(
queue.value.add(
items.map((item) => {
const rval = {
mode: "url",
Expand All @@ -236,7 +240,7 @@ function eventReset() {
counterAnnounce.value = 0;
counterSuccess.value = 0;
counterError.value = 0;
queue.reset();
queue.value.reset();
uploadItems.value = {};
extension.value = props.defaultExtension;
dbKey.value = props.defaultDbKey;
Expand Down Expand Up @@ -269,7 +273,7 @@ function eventStart() {
});
emit("progress", 0, "success");
counterRunning.value = counterAnnounce.value;
queue.start();
queue.value.start();
}
}
Expand All @@ -278,7 +282,7 @@ function eventStop() {
if (isRunning.value) {
emit("progress", null, "info");
queueStopping.value = true;
queue.stop();
queue.value.stop();
}
}
Expand Down
6 changes: 6 additions & 0 deletions lib/galaxy/config/sample/file_sources_conf.yml.sample
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
root: ${user.preferences['owncloud|root']}
login: ${user.preferences['owncloud|username']}
password: ${user.preferences['owncloud|password']}
# By default, the plugin will use temp files to avoid loading entire files into memory.
# You can change the directory here or omit to use the default temp directory.
temp_path: /your/temp/path
# Set writable to true if you have write access to this source
# and want to allow exporting files to it, by default is read only.
writable: false

- type: posix
root: '/data/5/galaxy_import/galaxy_user_data/covid-19/data/sequences/'
Expand Down
2 changes: 1 addition & 1 deletion lib/galaxy/dependencies/pinned-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ pysam==0.22.0 ; python_version >= "3.8" and python_version < "3.13"
python-dateutil==2.8.2 ; python_version >= "3.8" and python_version < "3.13"
python-jose==3.3.0 ; python_version >= "3.8" and python_version < "3.13"
python-magic==0.4.27 ; python_version >= "3.8" and python_version < "3.13"
python-multipart==0.0.6 ; python_version >= "3.8" and python_version < "3.13"
python-multipart==0.0.7 ; python_version >= "3.8" and python_version < "3.13"
python3-openid==3.2.0 ; python_version >= "3.8" and python_version < "3.13"
pytz==2023.3.post1 ; python_version >= "3.8" and python_version < "3.13"
pyyaml==6.0.1 ; python_version >= "3.8" and python_version < "3.13"
Expand Down
6 changes: 6 additions & 0 deletions lib/galaxy/files/sources/webdav.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
except ImportError:
WebDAVFS = None

import tempfile
from typing import (
Optional,
Union,
Expand All @@ -22,6 +23,11 @@ class WebDavFilesSource(PyFilesystem2FilesSource):

def _open_fs(self, user_context=None, opts: Optional[FilesSourceOptions] = None):
props = self._serialization_props(user_context)
use_temp_files = props.pop("use_temp_files", None)
if use_temp_files is None:
# Default to True to avoid memory issues with large files.
props["use_temp_files"] = True
props["temp_path"] = props.get("temp_path", tempfile.TemporaryDirectory(prefix="webdav_"))
extra_props: Union[FilesSourceProperties, dict] = opts.extra_props or {} if opts else {}
handle = WebDAVFS(**{**props, **extra_props})
return handle
Expand Down
Loading

0 comments on commit b278d8a

Please sign in to comment.