Skip to content

Commit c3f9fa7

Browse files
Merge remote-tracking branch 'upstream/release_24.2' into dev
2 parents e25355d + b8c902a commit c3f9fa7

20 files changed

Lines changed: 260 additions & 130 deletions

File tree

.github/workflows/publish_artifacts.yaml

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,46 @@ on:
33
release:
44
types: [released, prereleased]
55
jobs:
6-
build-and-publish:
6+
build-and-publish-pypi:
77
if: github.repository_owner == 'galaxyproject'
8-
name: build-and-publish
8+
name: Build and Publish to PyPI
99
runs-on: ubuntu-latest
1010
strategy:
1111
matrix:
1212
python-version: ['3.9']
1313
steps:
14+
- uses: actions/checkout@v4
1415
- uses: actions/setup-python@v5
1516
with:
1617
python-version: ${{ matrix.python-version }}
17-
- uses: actions/checkout@v4
1818
- name: Install script dependencies
1919
run: pip install galaxy-release-util
20-
- name: Build and publish
20+
- name: Build and publish to PyPI
2121
run: |
2222
galaxy-release-util build-and-upload --no-confirm
2323
env:
2424
TWINE_USERNAME: __token__
2525
TWINE_PASSWORD: ${{ github.event.release.prerelease && secrets.PYPI_TEST_TOKEN || secrets.PYPI_MAIN_TOKEN }}
2626
TWINE_REPOSITORY_URL: ${{ github.event.release.prerelease && 'https://test.pypi.org/legacy/' || 'https://upload.pypi.org/legacy/' }}
27+
28+
build-and-publish-npm:
29+
if: github.repository_owner == 'galaxyproject'
30+
name: Build and Publish to NPM
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/checkout@v4
34+
- uses: actions/setup-node@v4
35+
with:
36+
node-version: '18.12.1'
37+
cache: 'yarn'
38+
cache-dependency-path: 'client/yarn.lock'
39+
registry-url: 'https://registry.npmjs.org'
40+
- name: build client
41+
run: yarn && yarn build-production
42+
working-directory: 'client'
43+
- name: publish client
44+
if: "!github.event.release.prerelease"
45+
run: npm publish --provenance --access public
46+
working-directory: 'client'
47+
env:
48+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

client/src/api/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ export function isHDA(entry?: HistoryItemSummary): entry is HDASummary {
207207
/**
208208
* Returns true if the given entry is a top level HDCA and false for sub-collections.
209209
*/
210-
export function isHDCA(entry?: CollectionEntry): entry is HDCASummary {
210+
export function isHDCA(entry?: HistoryItemSummary | CollectionEntry): entry is HDCASummary {
211211
return (
212212
entry !== undefined && "history_content_type" in entry && entry.history_content_type === "dataset_collection"
213213
);

client/src/components/Form/Elements/FormData/FormData.vue

Lines changed: 118 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,16 @@ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
44
import { BAlert, BFormCheckbox } from "bootstrap-vue";
55
import { computed, onMounted, type Ref, ref, watch } from "vue";
66
7-
import { isDatasetElement, isDCE } from "@/api";
7+
import {
8+
type DCESummary,
9+
type HDAObject,
10+
type HistoryItemSummary,
11+
isDatasetElement,
12+
isDCE,
13+
isHDCA,
14+
isHistoryItem,
15+
} from "@/api";
16+
import type { HistoryContentType } from "@/api/datasets";
817
import { getGalaxyInstance } from "@/app";
918
import type { CollectionType } from "@/components/History/adapters/buildCollectionModal";
1019
import { useDatatypesMapper } from "@/composables/datatypesMapper";
@@ -27,6 +36,8 @@ type SelectOption = {
2736
value: DataOption | null;
2837
};
2938
39+
type HistoryOrCollectionItem = HistoryItemSummary | DCESummary;
40+
3041
const props = withDefaults(
3142
defineProps<{
3243
loading?: boolean;
@@ -51,7 +62,7 @@ const props = withDefaults(
5162
value: undefined,
5263
extensions: () => [],
5364
type: "data",
54-
collectionTypes: undefined,
65+
collectionTypes: () => [],
5566
flavor: undefined,
5667
tag: undefined,
5768
userDefinedTitle: undefined,
@@ -73,7 +84,7 @@ const currentField = ref(0);
7384
const currentHighlighting: Ref<string | null> = ref(null);
7485
7586
// Drag/Drop related values
76-
const dragData: Ref<EventData | null> = ref(null);
87+
const dragData: Ref<EventData[]> = ref([]);
7788
const dragTarget: Ref<EventTarget | null> = ref(null);
7889
7990
const workflowTab = ref("");
@@ -339,35 +350,50 @@ function getSourceType(val: DataOption) {
339350
}
340351
341352
/** Add values from drag/drop or data dialog sources */
342-
function handleIncoming(incoming: Record<string, unknown>, partial = true) {
353+
function handleIncoming(incoming: Record<string, unknown> | Record<string, unknown>[], partial = true) {
343354
if (incoming) {
344355
const values = Array.isArray(incoming) ? incoming : [incoming];
345-
const extensions = values.map((v) => v.extension || v.elements_datatypes).filter((v) => (v ? true : false));
356+
357+
// ensure all incoming values are isHistoryOrCollectionItem
358+
if (!values.every(isHistoryOrCollectionItem)) {
359+
return false;
360+
}
361+
362+
const extensions = Array.from(
363+
new Set(
364+
values
365+
.map(getExtensionsForItem)
366+
.flat()
367+
.filter((v) => v !== null && v !== undefined)
368+
)
369+
) as string[];
370+
346371
if (!canAcceptDatatype(extensions)) {
347372
return false;
348373
}
349-
if (values.some((v) => !canAcceptSrc(v.history_content_type, v.collection_type))) {
374+
if (
375+
values.some((v) => {
376+
const { historyContentType } = getSrcAndContentType(v);
377+
const collectionType = "collection_type" in v && v.collection_type ? v.collection_type : undefined;
378+
return !canAcceptSrc(historyContentType, collectionType);
379+
})
380+
) {
350381
return false;
351382
}
352383
if (values.length > 0) {
353384
const incomingValues: Array<DataOption> = [];
354-
values.forEach((v) => {
385+
values.forEach((currVal) => {
355386
// Map incoming objects to data option values
356-
let newSrc;
357-
if (isDCE(v)) {
358-
if (isDatasetElement(v)) {
359-
newSrc = SOURCE.DATASET;
360-
v = v.object;
361-
} else {
362-
newSrc = SOURCE.COLLECTION_ELEMENT;
363-
}
387+
const { newSrc, datasetCollectionDataset } = getSrcAndContentType(currVal);
388+
let v: HistoryOrCollectionItem | HDAObject;
389+
if (datasetCollectionDataset) {
390+
v = datasetCollectionDataset;
364391
} else {
365-
newSrc =
366-
v.src || (v.history_content_type === "dataset_collection" ? SOURCE.COLLECTION : SOURCE.DATASET);
392+
v = currVal;
367393
}
368-
const newHid = v.hid;
394+
const newHid = isHistoryItem(v) ? v.hid : undefined;
369395
const newId = v.id;
370-
const newName = v.name ? v.name : newId;
396+
const newName = isHistoryItem(v) && v.name ? v.name : newId;
371397
const newValue: DataOption = {
372398
id: newId,
373399
src: newSrc,
@@ -378,10 +404,11 @@ function handleIncoming(incoming: Record<string, unknown>, partial = true) {
378404
keep: true,
379405
tags: [],
380406
};
381-
if (v.collection_type && props.collectionTypes?.length > 0) {
382-
if (!props.collectionTypes.includes(v.collection_type)) {
407+
if (isHistoryItem(v) && isHDCA(v) && props.collectionTypes?.length > 0) {
408+
const itemCollectionType = v.collection_type;
409+
if (!props.collectionTypes.includes(itemCollectionType as CollectionType)) {
383410
const mapOverType = props.collectionTypes.find((collectionType) =>
384-
v.collection_type.endsWith(collectionType)
411+
itemCollectionType.endsWith(collectionType)
385412
);
386413
if (!mapOverType) {
387414
return false;
@@ -445,6 +472,9 @@ function onBrowse() {
445472
}
446473
447474
function canAcceptDatatype(itemDatatypes: string | Array<string>) {
475+
// TODO: Shouldn't we enforce a datatype (at least "data") because of the case:
476+
// What if the drop item is a `DCESummary`, then it has no extension (?) and we
477+
// pass it as a valid item regardless of its elements' datatypes.
448478
if (!(props.extensions?.length > 0) || props.extensions.includes("data")) {
449479
return true;
450480
}
@@ -463,7 +493,40 @@ function canAcceptDatatype(itemDatatypes: string | Array<string>) {
463493
return true;
464494
}
465495
466-
function canAcceptSrc(historyContentType: "dataset" | "dataset_collection", collectionType?: CollectionType) {
496+
/**
497+
* Given an element, determine the source and content type.
498+
* Also returns the collection element dataset object if it exists.
499+
*/
500+
function getSrcAndContentType(element: HistoryOrCollectionItem): {
501+
historyContentType: HistoryContentType;
502+
newSrc: string;
503+
datasetCollectionDataset: HDAObject | undefined;
504+
} {
505+
let historyContentType: HistoryContentType;
506+
let newSrc: string;
507+
let datasetCollectionDataset: HDAObject | undefined;
508+
if (isDCE(element)) {
509+
if (isDatasetElement(element)) {
510+
historyContentType = "dataset";
511+
newSrc = SOURCE.DATASET;
512+
datasetCollectionDataset = element.object;
513+
} else {
514+
historyContentType = "dataset_collection";
515+
newSrc = SOURCE.COLLECTION_ELEMENT;
516+
}
517+
} else {
518+
historyContentType = element.history_content_type;
519+
newSrc =
520+
"src" in element && typeof element.src === "string"
521+
? element.src
522+
: historyContentType === "dataset_collection"
523+
? SOURCE.COLLECTION
524+
: SOURCE.DATASET;
525+
}
526+
return { historyContentType, newSrc, datasetCollectionDataset };
527+
}
528+
529+
function canAcceptSrc(historyContentType: "dataset" | "dataset_collection", collectionType?: string) {
467530
if (historyContentType === "dataset") {
468531
// HDA can only be fed into data parameters, not collection parameters
469532
if (props.type === "data") {
@@ -485,7 +548,7 @@ function canAcceptSrc(historyContentType: "dataset" | "dataset_collection", coll
485548
// if no collection_type is set all collections are valid
486549
return true;
487550
} else {
488-
if (props.collectionTypes.includes(collectionType)) {
551+
if (props.collectionTypes.includes(collectionType as CollectionType)) {
489552
return true;
490553
}
491554
if (props.collectionTypes.some((element) => collectionType.endsWith(element))) {
@@ -511,31 +574,37 @@ const effectiveCollectionTypes = props.collectionTypes?.filter((collectionType)
511574
);
512575
const currentCollectionTypeTab = ref(effectiveCollectionTypes?.[0]);
513576
577+
/**
578+
* Get the extension(s) for a given item
579+
*/
580+
function getExtensionsForItem(item: HistoryOrCollectionItem): string | string[] | null {
581+
return "extension" in item ? item.extension : "elements_datatypes" in item ? item.elements_datatypes : null;
582+
}
583+
584+
function isHistoryOrCollectionItem(item: EventData): item is HistoryOrCollectionItem {
585+
return isHistoryItem(item) || isDCE(item);
586+
}
587+
514588
// Drag/Drop event handlers
515589
function onDragEnter(evt: DragEvent) {
516-
const eventData = eventStore.getDragData();
517-
if (eventData) {
518-
let eventFiles: any[];
519-
if (eventStore.multipleDragData) {
520-
eventFiles = Object.values(eventData);
521-
} else {
522-
eventFiles = [eventData];
523-
}
590+
const eventData = eventStore.getDragItems();
524591
592+
if (eventData?.length) {
525593
let highlightingState = "success";
526-
for (const eventFile of eventFiles) {
527-
const extensions = (eventFile.extension as string) || (eventFile.elements_datatypes as Array<string>);
528-
if (!canAcceptDatatype(extensions)) {
529-
highlightingState = "warning";
530-
$emit("alert", `${extensions} is not an acceptable format for this parameter.`);
531-
} else if (
532-
!canAcceptSrc(
533-
eventFile.history_content_type as "dataset" | "dataset_collection",
534-
eventFile.collection_type as CollectionType
535-
)
536-
) {
537-
highlightingState = "warning";
538-
$emit("alert", `${eventFile.history_content_type} is not an acceptable input type for this parameter.`);
594+
for (const item of eventData) {
595+
if (isHistoryOrCollectionItem(item)) {
596+
const extensions = getExtensionsForItem(item);
597+
const { historyContentType } = getSrcAndContentType(item);
598+
const collectionType =
599+
"collection_type" in item && item.collection_type ? item.collection_type : undefined;
600+
601+
if (extensions && !canAcceptDatatype(extensions)) {
602+
highlightingState = "warning";
603+
$emit("alert", `${extensions} is not an acceptable format for this parameter.`);
604+
} else if (!canAcceptSrc(historyContentType, collectionType)) {
605+
highlightingState = "warning";
606+
$emit("alert", `${historyContentType} is not an acceptable input type for this parameter.`);
607+
}
539608
}
540609
}
541610
currentHighlighting.value = highlightingState;
@@ -568,14 +637,8 @@ function onDragLeave(evt: DragEvent) {
568637
}
569638
570639
function onDrop(e: DragEvent) {
571-
if (dragData.value) {
572-
let accept = false;
573-
if (eventStore.multipleDragData) {
574-
accept = handleIncoming(Object.values(dragData.value) as any, false);
575-
} else {
576-
accept = handleIncoming(dragData.value);
577-
}
578-
if (accept) {
640+
if (dragData.value.length) {
641+
if (handleIncoming(dragData.value, dragData.value.length === 1)) {
579642
currentHighlighting.value = "success";
580643
if (props.workflowRun) {
581644
workflowTab.value = "view";
@@ -584,11 +647,11 @@ function onDrop(e: DragEvent) {
584647
currentHighlighting.value = "warning";
585648
}
586649
$emit("alert", undefined);
587-
dragData.value = null;
650+
dragData.value = [];
588651
clearHighlighting();
589652
} else if (props.workflowRun && e.dataTransfer?.files?.length) {
590653
$emit("alert", undefined);
591-
dragData.value = null;
654+
dragData.value = [];
592655
clearHighlighting();
593656
}
594657
}

client/src/components/Form/Elements/FormData/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export type DataOption = {
22
id: string;
3-
hid: number;
3+
hid?: number;
44
is_dataset?: boolean;
55
keep: boolean;
66
batch: boolean;

client/src/components/JobInformation/JobDetails.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<template>
22
<b-card>
33
<JobInformation :job_id="id" :include-times="true">
4+
<!-- only needed for admin job component -->
45
<tr v-if="hasTraceback">
56
<td>Traceback</td>
67
<td>

client/src/components/Workflow/WorkflowAnnotation.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ const workflowTags = computed(() => {
8585
</div>
8686
<slot name="middle-content" />
8787
<div class="d-flex align-items-center">
88-
<div class="d-flex flex-column align-items-end mr-2">
88+
<div class="d-flex flex-column align-items-end mr-2 flex-gapy-1">
8989
<WorkflowIndicators :workflow="workflow" published-view no-edit-time />
9090
<WorkflowInvocationsCount v-if="owned" class="mr-1" :workflow="workflow" />
9191
</div>

0 commit comments

Comments
 (0)