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 Jun 17, 2024
2 parents ce46c63 + 853ec92 commit ce6f746
Show file tree
Hide file tree
Showing 15 changed files with 209 additions and 42 deletions.
11 changes: 10 additions & 1 deletion client/src/api/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5512,6 +5512,15 @@ export interface components {
*/
url: string;
};
/** GroupUpdatePayload */
GroupUpdatePayload: {
/** name of the group */
name?: string | null;
/** role IDs */
role_ids?: string[] | null;
/** user IDs */
user_ids?: string[] | null;
};
/** GroupUserListResponse */
GroupUserListResponse: components["schemas"]["GroupUserResponse"][];
/** GroupUserResponse */
Expand Down Expand Up @@ -15744,7 +15753,7 @@ export interface operations {
};
requestBody: {
content: {
"application/json": components["schemas"]["GroupCreatePayload"];
"application/json": components["schemas"]["GroupUpdatePayload"];
};
};
responses: {
Expand Down
1 change: 1 addition & 0 deletions client/src/components/FilesDialog/FilesDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ onMounted(() => {
:is-busy="isBusy"
:items="items"
:items-provider="itemsProvider"
:provider-url="currentDirectory?.url"
:total-items="totalItems"
:modal-show="modalShow"
:modal-static="modalStatic"
Expand Down
4 changes: 4 additions & 0 deletions client/src/components/Form/Elements/FormData/FormData.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { mount } from "@vue/test-utils";
import { PiniaVuePlugin } from "pinia";
import { dispatchEvent, getLocalVue } from "tests/jest/helpers";

import { testDatatypesMapper } from "@/components/Datatypes/test_fixtures";
import { useDatatypesMapperStore } from "@/stores/datatypesMapperStore";
import { useEventStore } from "@/stores/eventStore";

import MountTarget from "./FormData.vue";
Expand All @@ -15,6 +17,8 @@ let eventStore;
function createTarget(propsData) {
const pinia = createTestingPinia({ stubActions: false });
eventStore = useEventStore();
const datatypesStore = useDatatypesMapperStore();
datatypesStore.datatypesMapper = testDatatypesMapper;
return mount(MountTarget, {
localVue,
propsData,
Expand Down
74 changes: 62 additions & 12 deletions client/src/components/Form/Elements/FormData/FormData.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { BAlert, BButton, BButtonGroup, BCollapse, BFormCheckbox, BTooltip } fro
import { computed, onMounted, type Ref, ref, watch } from "vue";
import { getGalaxyInstance } from "@/app";
import { useDatatypesMapper } from "@/composables/datatypesMapper";
import { useUid } from "@/composables/utils/uid";
import { type EventData, useEventStore } from "@/stores/eventStore";
import { orList } from "@/utils/strings";
Expand Down Expand Up @@ -35,6 +36,7 @@ const props = withDefaults(
};
extensions?: Array<string>;
type?: string;
collectionTypes?: Array<string>;
flavor?: string;
tag?: string;
}>(),
Expand All @@ -45,14 +47,16 @@ const props = withDefaults(
value: undefined,
extensions: () => [],
type: "data",
collectionTypes: undefined,
flavor: undefined,
tag: undefined,
}
);
const eventStore = useEventStore();
const { datatypesMapper } = useDatatypesMapper();
const $emit = defineEmits(["input"]);
const $emit = defineEmits(["input", "alert"]);
// Determines wether values should be processed as linked or unlinked
const currentLinked = ref(true);
Expand Down Expand Up @@ -302,6 +306,10 @@ function getSourceType(val: DataOption) {
function handleIncoming(incoming: Record<string, unknown>, partial = true) {
if (incoming) {
const values = Array.isArray(incoming) ? incoming : [incoming];
const extensions = values.map((v) => v.extension || v.elements_datatypes).filter((v) => (v ? true : false));
if (!canAcceptDatatype(extensions)) {
return false;
}
if (values.length > 0) {
const incomingValues: Array<DataOption> = [];
values.forEach((v) => {
Expand All @@ -311,14 +319,28 @@ function handleIncoming(incoming: Record<string, unknown>, partial = true) {
const newName = v.name ? v.name : newId;
const newSrc =
v.src || (v.history_content_type === "dataset_collection" ? SOURCE.COLLECTION : SOURCE.DATASET);
const newValue = {
const newValue: DataOption = {
id: newId,
src: newSrc,
batch: false,
map_over_type: undefined,
hid: newHid,
name: newName,
keep: true,
tags: [],
};
if (v.collection_type && props.collectionTypes?.length > 0) {
if (!props.collectionTypes.includes(v.collection_type)) {
const mapOverType = props.collectionTypes.find((collectionType) =>
v.collection_type.endsWith(collectionType)
);
if (!mapOverType) {
return false;
}
newValue["batch"] = true;
newValue["map_over_type"] = mapOverType;
}
}
// Verify that new value has corresponding option
const keepKey = `${newId}_${newSrc}`;
const existingOptions = props.options && props.options[newSrc];
Expand Down Expand Up @@ -349,6 +371,7 @@ function handleIncoming(incoming: Record<string, unknown>, partial = true) {
}
}
}
return true;
}
/**
Expand All @@ -372,10 +395,36 @@ function onBrowse() {
}
}
function canAcceptDatatype(itemDatatypes: string | Array<string>) {
if (!(props.extensions?.length > 0)) {
return true;
}
let datatypes: Array<string>;
if (!Array.isArray(itemDatatypes)) {
datatypes = [itemDatatypes];
} else {
datatypes = itemDatatypes;
}
const incompatibleItem = datatypes.find(
(extension) => !datatypesMapper.value?.isSubTypeOfAny(extension, props.extensions)
);
if (incompatibleItem) {
return false;
}
return true;
}
// Drag/Drop event handlers
function onDragEnter(evt: MouseEvent) {
const eventData = eventStore.getDragData();
if (eventData) {
const extensions = (eventData.extension as string) || (eventData.elements_datatypes as Array<string>);
if (!canAcceptDatatype(extensions)) {
currentHighlighting.value = "warning";
$emit("alert", `${extensions} is not an acceptable format for this parameter.`);
} else {
currentHighlighting.value = "success";
}
dragTarget.value = evt.target;
dragData.value = eventData;
}
Expand All @@ -384,23 +433,24 @@ function onDragEnter(evt: MouseEvent) {
function onDragLeave(evt: MouseEvent) {
if (dragTarget.value === evt.target) {
currentHighlighting.value = null;
}
}
function onDragOver() {
if (dragData.value !== null) {
currentHighlighting.value = "warning";
$emit("alert", undefined);
}
}
function onDrop() {
if (dragData.value) {
let accept = false;
if (eventStore.multipleDragData) {
handleIncoming(Object.values(dragData.value) as any, false);
accept = handleIncoming(Object.values(dragData.value) as any, false);
} else {
accept = handleIncoming(dragData.value);
}
if (accept) {
currentHighlighting.value = "success";
} else {
handleIncoming(dragData.value);
currentHighlighting.value = "warning";
}
currentHighlighting.value = "success";
$emit("alert", undefined);
dragData.value = null;
clearHighlighting();
}
Expand Down Expand Up @@ -468,7 +518,7 @@ const noOptionsWarningMessage = computed(() => {
:class="currentHighlighting && `ui-dragover-${currentHighlighting}`"
@dragenter.prevent="onDragEnter"
@dragleave.prevent="onDragLeave"
@dragover.prevent="onDragOver"
@dragover.prevent
@drop.prevent="onDrop">
<div class="d-flex flex-column">
<BButtonGroup v-if="variant && variant.length > 1" buttons class="align-self-start">
Expand Down
1 change: 1 addition & 0 deletions client/src/components/Form/Elements/FormData/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export type DataOption = {
hid: number;
is_dataset?: boolean;
keep: boolean;
batch: boolean;
map_over_type?: string;
name: string;
src: string;
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Form/FormElement.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe("FormElement", () => {
const error = wrapper.find(".ui-form-error-text");
expect(error.text()).toBe("error_text");

await wrapper.setProps({ error: "" });
await wrapper.setProps({ error: undefined });
const no_error = wrapper.findAll(".ui-form-error");
expect(no_error.length).toBe(0);

Expand Down
22 changes: 16 additions & 6 deletions client/src/components/Form/FormElement.vue
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ function onConnect() {
const isHidden = computed(() => attrs.value["hidden"]);
const elementId = computed(() => `form-element-${props.id}`);
const hasAlert = computed(() => Boolean(props.error || props.warning));
const hasAlert = computed(() => alerts.value.length > 0);
const showPreview = computed(() => (collapsed.value && attrs.value["collapsible_preview"]) || props.disabled);
const showField = computed(() => !collapsed.value && !props.disabled);
Expand Down Expand Up @@ -174,6 +174,16 @@ const isEmpty = computed(() => {
const isRequired = computed(() => attrs.value["optional"] === false);
const isRequiredType = computed(() => props.type !== "boolean");
const isOptional = computed(() => !isRequired.value && attrs.value["optional"] !== undefined);
const formAlert = ref<string>();
const alerts = computed(() => {
return [formAlert.value, props.error, props.warning]
.filter((v) => v !== undefined && v !== null)
.map((v) => linkify(sanitize(v!, { USE_PROFILES: { html: true } })));
});
function onAlert(value: string | undefined) {
formAlert.value = value;
}
</script>

<template>
Expand All @@ -182,11 +192,9 @@ const isOptional = computed(() => !isRequired.value && attrs.value["optional"] !
:id="elementId"
class="ui-form-element section-row"
:class="{ alert: hasAlert, 'alert-info': hasAlert }">
<div v-if="hasAlert" class="ui-form-error">
<div v-for="(alert, index) in alerts" :key="index" class="ui-form-error">
<FontAwesomeIcon class="mr-1" icon="fa-exclamation" />
<span
class="ui-form-error-text"
v-html="linkify(sanitize(props.error || props.warning, { USE_PROFILES: { html: true } }))" />
<span class="ui-form-error-text" v-html="alert" />
</div>

<div class="ui-form-title">
Expand Down Expand Up @@ -288,7 +296,9 @@ const isOptional = computed(() => !isRequired.value && attrs.value["optional"] !
:optional="attrs.optional"
:options="attrs.options"
:tag="attrs.tag"
:type="props.type" />
:type="props.type"
:collection-types="attrs.collection_types"
@alert="onAlert" />
<FormDrilldown
v-else-if="props.type === 'drill_down'"
:id="id"
Expand Down
12 changes: 12 additions & 0 deletions client/src/components/SelectionDialog/SelectionDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface Props {
isEncoded?: boolean;
items?: SelectionItem[];
itemsProvider?: ItemsProvider;
providerUrl?: string;
totalItems?: number;
leafIcon?: string;
modalShow?: boolean;
Expand All @@ -48,6 +49,7 @@ const props = withDefaults(defineProps<Props>(), {
isEncoded: false,
items: () => [],
itemsProvider: undefined,
providerUrl: undefined,
totalItems: 0,
leafIcon: "fa fa-file-o",
modalShow: true,
Expand Down Expand Up @@ -127,6 +129,16 @@ watch(
filtered(props.items);
}
);
watch(
() => props.providerUrl,
() => {
// We need to reset the current page when drilling down sub-folders
if (props.itemsProvider !== undefined) {
currentPage.value = 1;
}
}
);
</script>

<template>
Expand Down
21 changes: 15 additions & 6 deletions lib/galaxy/managers/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
from galaxy.model.db.user import get_users_by_ids
from galaxy.model.scoped_session import galaxy_scoped_session
from galaxy.schema.fields import Security
from galaxy.schema.groups import GroupCreatePayload
from galaxy.schema.groups import (
GroupCreatePayload,
GroupUpdatePayload,
)
from galaxy.structured_app import MinimalManagerApp


Expand Down Expand Up @@ -76,7 +79,7 @@ def show(self, trans: ProvidesAppContext, group_id: int):
item["roles_url"] = self._url_for(trans, "group_roles", group_id=encoded_id)
return item

def update(self, trans: ProvidesAppContext, group_id: int, payload: GroupCreatePayload):
def update(self, trans: ProvidesAppContext, group_id: int, payload: GroupUpdatePayload):
"""
Modifies a group.
"""
Expand All @@ -86,13 +89,19 @@ def update(self, trans: ProvidesAppContext, group_id: int, payload: GroupCreateP
self._check_duplicated_group_name(sa_session, name)
group.name = name
sa_session.add(group)
user_ids = payload.user_ids
users = get_users_by_ids(sa_session, user_ids)
role_ids = payload.role_ids
roles = get_roles_by_ids(sa_session, role_ids)

users = None
if payload.user_ids is not None:
users = get_users_by_ids(sa_session, payload.user_ids)

roles = None
if payload.role_ids is not None:
roles = get_roles_by_ids(sa_session, payload.role_ids)

self._app.security_agent.set_entity_group_associations(
groups=[group], roles=roles, users=users, delete_existing_assocs=False
)

with transaction(sa_session):
sa_session.commit()

Expand Down
Loading

0 comments on commit ce6f746

Please sign in to comment.