Skip to content

Commit

Permalink
Display DCE in job parameter component
Browse files Browse the repository at this point in the history
Addresses part of #15729
  • Loading branch information
mvdbeek committed Apr 24, 2023
1 parent 102aed5 commit af47306
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 10 deletions.
27 changes: 19 additions & 8 deletions client/src/components/History/Content/GenericItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,35 @@
<loading-span v-if="loading" message="Loading dataset" />
<div v-else>
<ContentItem
:id="item.hid"
:id="item.hid ?? item.element_index + 1"
is-history-item
:item="item"
:name="item.name"
:item="item?.object || item"
:name="item.name || item.element_identifier"
:expand-dataset="expandDataset"
:is-dataset="item.history_content_type == 'dataset'"
:is-dataset="item.history_content_type == 'dataset' || item.element_type == 'hda'"
@update:expand-dataset="expandDataset = $event"
@view-collection="viewCollection = !viewCollection"
@delete="onDelete(item)"
@undelete="onUndelete(item)"
@unhide="onUnhide(item)" />
<div v-if="viewCollection">
<GenericElement :dsc="item" />
<GenericElement :dsc="item?.object || item" />
</div>
</div>
</component>
</template>

<script>
import LoadingSpan from "components/LoadingSpan";
import { DatasetCollectionProvider, DatasetProvider } from "components/providers";
import { deleteContent, updateContentFields } from "components/History/model/queries";
import { DatasetCollectionProvider, DatasetProvider } from "@/components/providers";
import { DatasetCollectionElementProvider } from "@/components/providers/storeProviders";
import { deleteContent, updateContentFields } from "@/components/History/model/queries";
import ContentItem from "./ContentItem";
import GenericElement from "./GenericElement";
export default {
components: {
DatasetCollectionElementProvider,
ContentItem,
GenericElement,
DatasetProvider,
Expand All @@ -54,7 +56,16 @@ export default {
},
computed: {
providerComponent() {
return this.itemSrc == "hda" ? "DatasetProvider" : "DatasetCollectionProvider";
switch (this.itemSrc) {
case "hda":
return "DatasetProvider";
case "hdca":
return "DatasetCollectionProvider";
case "dce":
return "DatasetCollectionElementProvider";
default:
throw `Unknown element src ${this.itemSrc}`;
}
},
},
methods: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div>
<div v-for="(elVal, pvIndex) in parameter_value" :key="pvIndex">
<generic-history-item
v-if="['hda', 'hdca'].includes(elVal.src)"
v-if="['hda', 'hdca', 'dce'].includes(elVal.src)"
:item-id="elVal.id"
:item-src="elVal.src" />
<span v-else> {{ elVal.hid }}: {{ elVal.name }} </span>
Expand Down
10 changes: 10 additions & 0 deletions client/src/components/providers/storeProviders.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const SimpleProviderMixin = {
loading: this.loading,
item: this.item,
save: this.save,
result: this.item,
});
},
};
Expand Down Expand Up @@ -131,6 +132,15 @@ export const JobProvider = {
},
};

export const DatasetCollectionElementProvider = {
mixins: [SimpleProviderMixin],
computed: {
url() {
return prependPath(`api/dataset_collection_element/${this.id}`);
},
},
};

/**
* Provider component interface to the actual stores i.e. history items and collection elements stores.
* @param {String} storeAction The store action is executed when the consuming component e.g. the history panel, changes the provider props.
Expand Down
31 changes: 31 additions & 0 deletions client/src/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export interface paths {
*/
put: operations["reload_toolbox_api_configuration_toolbox_put"];
};
"/api/dataset_collection_element/{dce_id}": {
/** Content */
get: operations["content_api_dataset_collection_element__dce_id__get"];
};
"/api/dataset_collections": {
/** Create a new dataset collection instance. */
post: operations["create_api_dataset_collections_post"];
Expand Down Expand Up @@ -7544,6 +7548,33 @@ export interface operations {
};
};
};
content_api_dataset_collection_element__dce_id__get: {
/** Content */
parameters: {
/** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */
header?: {
"run-as"?: string;
};
/** @description The encoded identifier of the dataset collection element. */
path: {
dce_id: string;
};
};
responses: {
/** @description Successful Response */
200: {
content: {
"application/json": components["schemas"]["DCESummary"];
};
};
/** @description Validation Error */
422: {
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
create_api_dataset_collections_post: {
/** Create a new dataset collection instance. */
parameters?: {
Expand Down
2 changes: 1 addition & 1 deletion lib/galaxy/managers/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -903,7 +903,7 @@ def inputs_recursive(input_params, param_values, depth=1, upgrade_messages=None)
value=f"{len(param_values[input.name])} uploaded datasets",
)
)
elif input.type == "data":
elif input.type == "data" or input.type == "data_collection":
value = []
for element in listify(param_values[input.name]):
encoded_id = trans.security.encode_id(element.id)
Expand Down
9 changes: 9 additions & 0 deletions lib/galaxy/model/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
datetime,
timedelta,
)
from typing import List

from sqlalchemy import (
and_,
Expand Down Expand Up @@ -519,6 +520,14 @@ def can_access_datasets(self, user_roles, action_tuples):

return True

def can_access_collection(
self, user_roles: List[galaxy.model.Role], collection: galaxy.model.DatasetCollection
):
action_tuples = collection.dataset_action_tuples
if not self.can_access_datasets(user_roles, action_tuples):
return False
return True

def can_manage_dataset(self, roles, dataset):
return self.allow_action(roles, self.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset)

Expand Down
14 changes: 14 additions & 0 deletions lib/galaxy/webapps/galaxy/api/dataset_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
AnyHDCA,
CreateNewCollectionPayload,
DatasetCollectionInstanceType,
DCESummary,
HDCADetailed,
)
from galaxy.webapps.galaxy.api import (
Expand All @@ -36,6 +37,11 @@
..., description="The encoded identifier of the dataset collection."
)


DatasetCollectionElementIdPathParam: DecodedDatabaseIdField = Path(
..., description="The encoded identifier of the dataset collection element."
)

InstanceTypeQueryParam: DatasetCollectionInstanceType = Query(
default="history",
description="The type of collection instance. Either `history` (default) or `library`.",
Expand Down Expand Up @@ -129,3 +135,11 @@ def contents(
),
) -> DatasetCollectionContentElements:
return self.service.contents(trans, hdca_id, parent_id, instance_type, limit, offset)

@router.get("/api/dataset_collection_element/{dce_id}")
def content(
self,
trans: ProvidesHistoryContext = DependsOnTrans,
dce_id: DecodedDatabaseIdField = DatasetCollectionElementIdPathParam,
) -> DCESummary:
return self.service.dce_content(trans, dce_id)
12 changes: 12 additions & 0 deletions lib/galaxy/webapps/galaxy/services/dataset_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from galaxy.managers.context import ProvidesHistoryContext
from galaxy.managers.hdcas import HDCAManager
from galaxy.managers.histories import HistoryManager
from galaxy.model import DatasetCollectionElement
from galaxy.schema.fields import (
DecodedDatabaseIdField,
ModelClassField,
Expand Down Expand Up @@ -208,6 +209,17 @@ def show(
)
return rval

def dce_content(self, trans: ProvidesHistoryContext, dce_id: DecodedDatabaseIdField) -> DCESummary:
dce: Optional[DatasetCollectionElement] = trans.model.session.query(DatasetCollectionElement).get(dce_id)
if not dce:
raise exceptions.ObjectNotFound("No DatasetCollectionElement found")
if not trans.user_is_admin:
collection = dce.child_collection or dce.collection
if not trans.app.security_agent.can_access_collection(trans.get_current_user_roles(), collection):
raise exceptions.ItemAccessibilityException("Collection not accessible by user.")
serialized_dce = dictify_element_reference(dce, recursive=False, security=trans.security)
return trans.security.encode_all_ids(serialized_dce, recursive=True)

def contents(
self,
trans: ProvidesHistoryContext,
Expand Down
31 changes: 31 additions & 0 deletions lib/galaxy_test/api/test_dataset_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,37 @@ def test_hda_security(self):
create_response = self._post("dataset_collections", payload, json=True)
self._assert_status_code_is(create_response, 403)

def test_dataset_collection_element_security(self):
with self.dataset_populator.test_history(require_new=False) as history_id:
dataset_collection = self.dataset_collection_populator.create_list_of_list_in_history(
history_id,
collection_type="list:list:list",
wait=True,
).json()
first_element = dataset_collection["elements"][0]
assert first_element["model_class"] == "DatasetCollectionElement"
assert first_element["element_type"] == "dataset_collection"
first_element_url = f"/api/dataset_collection_element/{first_element['id']}"
with self._different_user():
assert self._get(first_element_url).status_code == 403
collection_dce_response = self._get(first_element_url)
collection_dce_response.raise_for_status()
collection_dce = collection_dce_response.json()
assert collection_dce["model_class"] == "DatasetCollectionElement"
assert collection_dce["element_type"] == "dataset_collection"
fist_dataset_element = first_element["object"]["elements"][0]["object"]["elements"][0]
assert fist_dataset_element["model_class"] == "DatasetCollectionElement"
assert fist_dataset_element["element_type"] == "hda"
first_dataset_element_url = f"/api/dataset_collection_element/{fist_dataset_element['id']}"
with self._different_user():
assert self._get(first_dataset_element_url).status_code == 403
dataset_dce_response = self._get(first_dataset_element_url)
dataset_dce_response.raise_for_status()
dataset_dce = dataset_dce_response.json()
assert dataset_dce["model_class"] == "DatasetCollectionElement"
assert dataset_dce["element_type"] == "hda"
assert dataset_dce["object"]["model_class"] == "HistoryDatasetAssociation"

def test_enforces_unique_names(self):
with self.dataset_populator.test_history(require_new=False) as history_id:
element_identifiers = self.dataset_collection_populator.list_identifiers(history_id)
Expand Down

0 comments on commit af47306

Please sign in to comment.