mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge branch 'release_22.05' into dev
This commit is contained in:
@@ -84,7 +84,7 @@
|
||||
<DatasetDetails
|
||||
v-if="expandDataset"
|
||||
:dataset="item"
|
||||
:show-highlight="isHistoryItem && isHistPanel"
|
||||
:show-highlight="isHistoryItem"
|
||||
@edit="onEdit"
|
||||
@toggleHighlights="toggleHighlights" />
|
||||
</b-collapse>
|
||||
@@ -119,8 +119,7 @@ export default {
|
||||
highlight: { type: String, default: null },
|
||||
id: { type: Number, required: true },
|
||||
isDataset: { type: Boolean, default: true },
|
||||
isHistoryItem: { type: Boolean, default: true },
|
||||
isHistPanel: { type: Boolean, default: false },
|
||||
isHistoryItem: { type: Boolean, default: false },
|
||||
item: { type: Object, required: true },
|
||||
name: { type: String, required: true },
|
||||
selected: { type: Boolean, default: false },
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
:name="item.element_identifier"
|
||||
:is-dataset="item.element_type == 'hda'"
|
||||
:expand-dataset="expandDataset"
|
||||
:is-history-item="false"
|
||||
@update:expand-dataset="expandDataset = $event"
|
||||
@view-collection="viewCollection = !viewCollection" />
|
||||
<GenericItem v-if="viewCollection" :item-id="item.object.id" :item-src="item.object.history_content_type" />
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<loading-span v-if="loading" message="Loading dataset" />
|
||||
<div v-else>
|
||||
<ContentItem
|
||||
is-history-item
|
||||
:id="item.hid"
|
||||
:item="item"
|
||||
:name="item.name"
|
||||
@@ -10,9 +11,9 @@
|
||||
:is-dataset="item.history_content_type == 'dataset'"
|
||||
@update:expand-dataset="expandDataset = $event"
|
||||
@view-collection="viewCollection = !viewCollection"
|
||||
@delete="onDelete"
|
||||
@undelete="onUndelete"
|
||||
@unhide="onUnhide" />
|
||||
@delete="onDelete(item)"
|
||||
@undelete="onUndelete(item)"
|
||||
@unhide="onUnhide(item)" />
|
||||
<div v-if="viewCollection">
|
||||
<div v-for="(collectionItem, collectionIndex) in item.elements" :key="collectionIndex">
|
||||
<GenericElement :item="collectionItem" />
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
:name="item.element_identifier"
|
||||
:expand-dataset="isExpanded(item)"
|
||||
:is-dataset="item.element_type == 'hda'"
|
||||
:is-history-item="false"
|
||||
@update:expand-dataset="setExpanded(item, $event)"
|
||||
@view-collection="onViewSubCollection" />
|
||||
</template>
|
||||
|
||||
@@ -100,12 +100,12 @@
|
||||
<template v-slot:item="{ item, currentOffset }">
|
||||
<ContentItem
|
||||
v-if="!invisible[item.hid]"
|
||||
is-history-item
|
||||
:id="item.hid"
|
||||
:item="item"
|
||||
:name="item.name"
|
||||
:expand-dataset="isExpanded(item)"
|
||||
:is-dataset="isDataset(item)"
|
||||
is-hist-panel
|
||||
:highlight="getHighlight(item)"
|
||||
:selected="isSelected(item)"
|
||||
:selectable="showSelection"
|
||||
|
||||
@@ -57,10 +57,11 @@ export class TagService {
|
||||
if (!tag.valid) {
|
||||
throw new Error("Invalid tag");
|
||||
}
|
||||
const url = `${getAppRoot()}tag/add_tag_async?item_id=${id}&item_class=${itemClass}&context=${context}&new_tag=${
|
||||
tag.text
|
||||
}`;
|
||||
const response = await axios.get(url);
|
||||
const url = `${getAppRoot()}tag/add_tag_async`;
|
||||
const config = {
|
||||
params: { item_id: id, item_class: itemClass, context: context, new_tag: tag.text },
|
||||
};
|
||||
const response = await axios.get(url, config);
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Unable to save tag: ${tag}`);
|
||||
}
|
||||
@@ -75,10 +76,9 @@ export class TagService {
|
||||
async delete(rawTag) {
|
||||
const { id, itemClass, context } = this;
|
||||
const tag = createTag(rawTag);
|
||||
const url = `${getAppRoot()}tag/remove_tag_async?item_id=${id}&item_class=${itemClass}&context=${context}&tag_name=${
|
||||
tag.text
|
||||
}`;
|
||||
const response = await axios.get(url);
|
||||
const url = `${getAppRoot()}tag/remove_tag_async`;
|
||||
const config = { params: { item_id: id, item_class: itemClass, context: context, tag_name: tag.text } };
|
||||
const response = await axios.get(url, config);
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Unable to delete tag: ${tag}`);
|
||||
}
|
||||
@@ -92,8 +92,9 @@ export class TagService {
|
||||
*/
|
||||
async autocomplete(searchText) {
|
||||
const { id, itemClass } = this;
|
||||
const url = `${getAppRoot()}tag/tag_autocomplete_data?item_id=${id}&item_class=${itemClass}&q=${searchText}`;
|
||||
const response = await axios.get(url);
|
||||
const url = `${getAppRoot()}tag/tag_autocomplete_data`;
|
||||
const config = { params: { item_id: id, item_class: itemClass, q: searchText } };
|
||||
const response = await axios.get(url, config);
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Unable to retrieve autocomplete tags for search string: ${searchText}`);
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@ import axios from "axios";
|
||||
import MockAdapter from "axios-mock-adapter";
|
||||
import { TagService } from "./tagService";
|
||||
import { createTag } from "./model";
|
||||
//import { interval } from "rxjs";
|
||||
//import { take, takeUntil } from "rxjs/operators";
|
||||
// test response
|
||||
import autocompleteResponse from "./testData/autocompleteResponse.txt";
|
||||
|
||||
describe("Tags/tagService.js", () => {
|
||||
@@ -15,6 +12,12 @@ describe("Tags/tagService.js", () => {
|
||||
context: "",
|
||||
debounceInterval: 50, // shorter value than default for unit tests
|
||||
};
|
||||
const testLabel = "fo0bar123";
|
||||
const expectedParams = {
|
||||
item_id: 123,
|
||||
item_class: "fooClass",
|
||||
context: "",
|
||||
};
|
||||
|
||||
const svc = new TagService(svcParams);
|
||||
beforeEach(() => {
|
||||
@@ -26,43 +29,43 @@ describe("Tags/tagService.js", () => {
|
||||
});
|
||||
|
||||
describe("save", () => {
|
||||
const testLabel = "fo0bar123";
|
||||
const testTag = createTag(testLabel);
|
||||
|
||||
const { id, itemClass, context } = svcParams;
|
||||
const expectedSaveUrl = `/tag/add_tag_async?item_id=${id}&item_class=${itemClass}&context=${context}&new_tag=${testLabel}`;
|
||||
const expectedSaveUrl = `/tag/add_tag_async`;
|
||||
|
||||
it("should save a string tag", async () => {
|
||||
const savedTag = await svc.save(testLabel);
|
||||
expect(savedTag.text).toBe(testLabel);
|
||||
expect(axiosMock.history.get[0].url).toBe(expectedSaveUrl);
|
||||
expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, new_tag: testLabel });
|
||||
});
|
||||
|
||||
it("should save an object tag", async () => {
|
||||
const savedTag = await svc.save(testTag);
|
||||
expect(savedTag.text).toBe(testLabel);
|
||||
expect(axiosMock.history.get[0].url).toBe(expectedSaveUrl);
|
||||
expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, new_tag: testLabel });
|
||||
});
|
||||
|
||||
// TODO: test error conditions
|
||||
});
|
||||
describe("delete", () => {
|
||||
const testLabel = "fo0bar123";
|
||||
const testTag = createTag(testLabel);
|
||||
|
||||
const { id, itemClass, context } = svcParams;
|
||||
const expectedDeleteUrl = `/tag/remove_tag_async?item_id=${id}&item_class=${itemClass}&context=${context}&tag_name=${testLabel}`;
|
||||
const expectedDeleteUrl = `/tag/remove_tag_async`;
|
||||
|
||||
it("should delete a text tag", async () => {
|
||||
const result = await svc.delete(testTag);
|
||||
expect(result).toBeTruthy();
|
||||
expect(axiosMock.history.get[0].url).toBe(expectedDeleteUrl);
|
||||
expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, tag_name: testLabel });
|
||||
});
|
||||
|
||||
it("should delete an object tag", async () => {
|
||||
const result = await svc.delete(testTag);
|
||||
expect(result).toBeTruthy();
|
||||
expect(axiosMock.history.get[0].url).toBe(expectedDeleteUrl);
|
||||
expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, tag_name: testLabel });
|
||||
});
|
||||
|
||||
// TODO: test error conditions
|
||||
@@ -70,8 +73,7 @@ describe("Tags/tagService.js", () => {
|
||||
|
||||
describe("autocomplete", () => {
|
||||
const searchString = "foo";
|
||||
const { id, itemClass } = svcParams;
|
||||
const expectedSearchUrl = `/tag/tag_autocomplete_data?item_id=${id}&item_class=${itemClass}&q=${searchString}`;
|
||||
const expectedSearchUrl = `/tag/tag_autocomplete_data`;
|
||||
|
||||
const checkAutocompleteResult = (result) => {
|
||||
expect(result).toBeTruthy();
|
||||
@@ -83,46 +85,14 @@ describe("Tags/tagService.js", () => {
|
||||
|
||||
// straight ajax request, unused in practice, but it's easier to
|
||||
// test this call if we just expose it
|
||||
const { ...searchParams } = expectedParams;
|
||||
delete searchParams.context;
|
||||
|
||||
it("ajax call should return tag objects", async () => {
|
||||
const result = await svc.autocomplete(searchString);
|
||||
expect(axiosMock.history.get[0].url).toBe(expectedSearchUrl);
|
||||
expect(axiosMock.history.get[0].params).toEqual({ ...searchParams, q: searchString });
|
||||
checkAutocompleteResult(result);
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
// hit the search input with multiple entries, only one ajax call
|
||||
// should result because of debouncing
|
||||
it("should debounce autocomplete search inputs", (done) => {
|
||||
// stub ajax request to return the success response if the
|
||||
// searchString is the expected input
|
||||
|
||||
// take first emission, debouncing should guarantee the first
|
||||
// emission is the last thing we sent, should be searchString
|
||||
const timer$ = interval(100).pipe(take(1));
|
||||
const option$ = svc.autocompleteOptions.pipe(takeUntil(timer$));
|
||||
|
||||
const nextHandler = jest.fn();
|
||||
|
||||
option$.subscribe({
|
||||
next: nextHandler,
|
||||
error: (err) => console.warn("error", err),
|
||||
complete: () => {
|
||||
expect(nextHandler).toHaveBeenCalledTimes(1);
|
||||
expect(axiosMock.history.get.length).toBe(1);
|
||||
done();
|
||||
},
|
||||
});
|
||||
|
||||
// spam a bunch of key inputs followed by the correct input
|
||||
const spamCount = 2 + Math.floor(Math.random() * 10);
|
||||
for (let i = 0; i < spamCount; i++) {
|
||||
const spamVal = new String(Math.random());
|
||||
svc.autocompleteSearchText = spamVal;
|
||||
}
|
||||
svc.autocompleteSearchText = searchString;
|
||||
});
|
||||
|
||||
*/
|
||||
});
|
||||
});
|
||||
|
||||
@@ -86,6 +86,9 @@ export default {
|
||||
isLast() {
|
||||
return this.currentIndex === this.steps.length - 1;
|
||||
},
|
||||
hasBegun() {
|
||||
return this.currentIndex >= 1;
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener("keyup", this.handleKeyup);
|
||||
@@ -105,12 +108,15 @@ export default {
|
||||
}
|
||||
},
|
||||
loginRequired(user) {
|
||||
return this.requirements.indexOf("logged_in") >= 0 && user.isAnonymous;
|
||||
return !this.hasBegun && this.requirements.indexOf("logged_in") >= 0 && user.isAnonymous;
|
||||
},
|
||||
adminRequired(user) {
|
||||
return this.requirements.indexOf("admin") >= 0 && !user.is_admin;
|
||||
return !this.hasBegun && this.requirements.indexOf("admin") >= 0 && !user.is_admin;
|
||||
},
|
||||
newHistoryRequired(history) {
|
||||
if (this.hasBegun) {
|
||||
return false;
|
||||
}
|
||||
const hasNewNistoryRequirement = this.requirements.indexOf("new_history") >= 0;
|
||||
if (!hasNewNistoryRequirement) {
|
||||
return false;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { mountVueComponent } from "utils/mountVueComponent";
|
||||
import Tour from "./Tour";
|
||||
|
||||
// delays and maximum number of attempts to wait for element
|
||||
const attempts = 100;
|
||||
const attempts = 200;
|
||||
const delay = 200;
|
||||
|
||||
// return tour data
|
||||
|
||||
@@ -75,7 +75,7 @@ var DatasetAssociation = Backbone.Model.extend(BASE_MVC.LoggableMixin).extend(
|
||||
display: `datasets/${id}/display/?preview=True`,
|
||||
edit: `datasets/edit?dataset_id=${id}`,
|
||||
download: `api/datasets/${id}/display${this._downloadQueryParameters()}`,
|
||||
report_error: `dataset/errors?id=${id}`,
|
||||
report_error: `datasets/error?dataset_id=${id}`,
|
||||
rerun: `tool_runner/rerun?id=${id}`,
|
||||
show_params: `datasets/${id}/details`,
|
||||
visualization: "visualization",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import json
|
||||
import logging
|
||||
import typing
|
||||
from datetime import datetime
|
||||
|
||||
from boltons.iterutils import remap
|
||||
from pydantic import (
|
||||
@@ -107,7 +108,7 @@ class JobManager:
|
||||
|
||||
def build_and_apply_filters(query, objects, filter_func):
|
||||
if objects is not None:
|
||||
if isinstance(objects, str):
|
||||
if isinstance(objects, str) or isinstance(objects, datetime):
|
||||
query = query.filter(filter_func(objects))
|
||||
elif isinstance(objects, list):
|
||||
t = []
|
||||
|
||||
@@ -500,7 +500,7 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
def item_permission_map_for_add(self, trans, user_roles, libitems):
|
||||
return self.allow_action_on_libitems(trans, user_roles, self.permitted_actions.LIBRARY_ADD, libitems)
|
||||
|
||||
def can_access_dataset(self, user_roles, dataset):
|
||||
def can_access_dataset(self, user_roles, dataset: galaxy.model.Dataset):
|
||||
# SM: dataset_is_public will access dataset.actions, which is a
|
||||
# backref that causes a query to be made to DatasetPermissions
|
||||
retval = self.dataset_is_public(dataset) or self.allow_action(
|
||||
@@ -1122,7 +1122,7 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
if not dataset.purged and not self.dataset_is_public(dataset):
|
||||
self.make_dataset_public(dataset)
|
||||
|
||||
def dataset_is_public(self, dataset):
|
||||
def dataset_is_public(self, dataset: galaxy.model.Dataset):
|
||||
"""
|
||||
A dataset is considered public if there are no "access" actions
|
||||
associated with it. Any other actions ( 'manage permissions',
|
||||
|
||||
@@ -653,9 +653,11 @@ class DiskObjectStore(ConcreteObjectStore):
|
||||
if entire_dir and (extra_dir or obj_dir):
|
||||
shutil.rmtree(path)
|
||||
return True
|
||||
if self._exists(obj, **kwargs):
|
||||
os.remove(path)
|
||||
return True
|
||||
os.remove(path)
|
||||
return True
|
||||
except FileNotFoundError:
|
||||
# Absolutely possible that a delete request races, but that's "fine".
|
||||
return True
|
||||
except OSError as ex:
|
||||
log.critical(f"{self.__get_filename(obj, **kwargs)} delete error {ex}")
|
||||
return False
|
||||
|
||||
@@ -1112,8 +1112,8 @@ class JobIndexQueryPayload(Model):
|
||||
user_id: Optional[DecodedDatabaseIdField] = None
|
||||
tool_ids: Optional[List[str]] = None
|
||||
tool_ids_like: Optional[List[str]] = None
|
||||
date_range_min: Optional[str] = None
|
||||
date_range_max: Optional[str] = None
|
||||
date_range_min: Optional[datetime] = None
|
||||
date_range_max: Optional[datetime] = None
|
||||
history_id: Optional[DecodedDatabaseIdField] = None
|
||||
workflow_id: Optional[DecodedDatabaseIdField] = None
|
||||
invocation_id: Optional[DecodedDatabaseIdField] = None
|
||||
|
||||
@@ -120,7 +120,10 @@ from galaxy.util.bunch import Bunch
|
||||
from galaxy.util.dictifiable import Dictifiable
|
||||
from galaxy.util.expressions import ExpressionContext
|
||||
from galaxy.util.form_builder import SelectField
|
||||
from galaxy.util.json import safe_loads
|
||||
from galaxy.util.json import (
|
||||
safe_loads,
|
||||
swap_inf_nan,
|
||||
)
|
||||
from galaxy.util.rules_dsl import RuleSet
|
||||
from galaxy.util.template import (
|
||||
fill_template,
|
||||
@@ -2461,7 +2464,7 @@ class Tool(Dictifiable):
|
||||
"enctype": self.enctype,
|
||||
}
|
||||
)
|
||||
return tool_model
|
||||
return swap_inf_nan(tool_model)
|
||||
|
||||
def populate_model(self, request_context, inputs, state_inputs, group_inputs, other_values=None):
|
||||
"""
|
||||
|
||||
@@ -9,6 +9,7 @@ from collections.abc import (
|
||||
Mapping,
|
||||
Sequence,
|
||||
)
|
||||
from decimal import Decimal
|
||||
|
||||
from ..util import unicodify
|
||||
|
||||
@@ -22,7 +23,8 @@ from_json_string = json.loads
|
||||
|
||||
def swap_inf_nan(val):
|
||||
"""
|
||||
This takes an arbitrary object and preps it for jsonifying safely, templating Inf/NaN.
|
||||
This takes an arbitrary object and preps it for jsonifying safely, templating Inf/NaN and
|
||||
casting Decimal instances as strings.
|
||||
"""
|
||||
if isinstance(val, str):
|
||||
# basestring first, because it's a sequence and would otherwise get caught below.
|
||||
@@ -40,6 +42,8 @@ def swap_inf_nan(val):
|
||||
return "__-Infinity__"
|
||||
else:
|
||||
return val
|
||||
elif isinstance(val, Decimal):
|
||||
return str(val)
|
||||
else:
|
||||
return val
|
||||
|
||||
@@ -63,12 +67,12 @@ def safe_dumps(obj, **kwargs):
|
||||
"""
|
||||
This is a wrapper around dumps that encodes Infinity and NaN values. It's a
|
||||
fairly rare case (which will be low in request volume). Basically, we tell
|
||||
json.dumps to blow up if it encounters Infinity/NaN, and we 'fix' it before
|
||||
re-encoding.
|
||||
json.dumps to blow up if it encounters Infinity/NaN, or Decimal values
|
||||
and we 'fix' it before re-encoding.
|
||||
"""
|
||||
try:
|
||||
dumped = json.dumps(obj, allow_nan=False, **kwargs)
|
||||
except ValueError:
|
||||
except (ValueError, TypeError):
|
||||
obj = swap_inf_nan(obj)
|
||||
dumped = json.dumps(obj, allow_nan=False, **kwargs)
|
||||
if kwargs.get("escape_closing_tags", True):
|
||||
|
||||
@@ -5,6 +5,7 @@ API operations on a jobs.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import (
|
||||
Any,
|
||||
Dict,
|
||||
@@ -98,13 +99,13 @@ ToolIdLikeQueryParam: Optional[str] = Query(
|
||||
description="Limit listing of jobs to those that match one of the included tool ID sql-like patterns. If none, all are returned",
|
||||
)
|
||||
|
||||
DateRangeMinQueryParam: Optional[str] = Query(
|
||||
DateRangeMinQueryParam: Optional[datetime] = Query(
|
||||
default=None,
|
||||
title="Date Range Minimum",
|
||||
description="Limit listing of jobs to those that are updated after specified date (e.g. '2014-01-01')",
|
||||
)
|
||||
|
||||
DateRangeMaxQueryParam: Optional[str] = Query(
|
||||
DateRangeMaxQueryParam: Optional[datetime] = Query(
|
||||
default=None,
|
||||
title="Date Range Maximum",
|
||||
description="Limit listing of jobs to those that are updated before specified date (e.g. '2014-01-01')",
|
||||
@@ -186,8 +187,8 @@ class FastAPIJobs:
|
||||
view: JobIndexViewEnum = ViewQueryParam,
|
||||
tool_id: Optional[str] = ToolIdQueryParam,
|
||||
tool_id_like: Optional[str] = ToolIdLikeQueryParam,
|
||||
date_range_min: Optional[str] = DateRangeMinQueryParam,
|
||||
date_range_max: Optional[str] = DateRangeMaxQueryParam,
|
||||
date_range_min: Optional[datetime] = DateRangeMinQueryParam,
|
||||
date_range_max: Optional[datetime] = DateRangeMaxQueryParam,
|
||||
history_id: Optional[EncodedDatabaseIdField] = HistoryIdQueryParam,
|
||||
workflow_id: Optional[EncodedDatabaseIdField] = WorkflowIdQueryParam,
|
||||
invocation_id: Optional[EncodedDatabaseIdField] = InvocationIdQueryParam,
|
||||
|
||||
@@ -10,7 +10,6 @@ from markupsafe import escape
|
||||
|
||||
from galaxy import (
|
||||
datatypes,
|
||||
model,
|
||||
util,
|
||||
web,
|
||||
)
|
||||
@@ -88,16 +87,6 @@ class DatasetInterface(BaseUIController, UsesAnnotations, UsesItemRatings, UsesE
|
||||
roles, dataset_association.dataset
|
||||
)
|
||||
|
||||
@web.expose
|
||||
def errors(self, trans, id):
|
||||
hda = trans.sa_session.query(model.HistoryDatasetAssociation).get(self.decode_id(id))
|
||||
|
||||
if not hda or not self._can_access_dataset(trans, hda):
|
||||
return trans.show_error_message(
|
||||
"Either this dataset does not exist or you do not have permission to access it."
|
||||
)
|
||||
return trans.fill_template("dataset/errors.mako", hda=hda)
|
||||
|
||||
@web.expose
|
||||
def stdout(self, trans, dataset_id=None, **kwargs):
|
||||
trans.response.set_content_type("text/plain")
|
||||
@@ -713,6 +702,10 @@ class DatasetInterface(BaseUIController, UsesAnnotations, UsesItemRatings, UsesE
|
||||
**kwds,
|
||||
):
|
||||
"""Access to external display applications"""
|
||||
if None in [app_name, link_name]:
|
||||
return trans.show_error_message("A display application name and link name must be provided.")
|
||||
app_name = unquote_plus(app_name)
|
||||
link_name = unquote_plus(link_name)
|
||||
# Build list of parameters to pass in to display application logic (app_kwds)
|
||||
app_kwds = {}
|
||||
for name, value in dict(kwds).items(): # clone kwds because we remove stuff as we go.
|
||||
@@ -734,10 +727,6 @@ class DatasetInterface(BaseUIController, UsesAnnotations, UsesItemRatings, UsesE
|
||||
else:
|
||||
user_roles = []
|
||||
# Decode application name and link name
|
||||
app_name = unquote_plus(app_name)
|
||||
link_name = unquote_plus(link_name)
|
||||
if None in [app_name, link_name]:
|
||||
return trans.show_error_message("A display application name and link name must be provided.")
|
||||
if self._can_access_dataset(trans, data, additional_roles=user_roles):
|
||||
msg = []
|
||||
preparable_steps = []
|
||||
|
||||
@@ -106,8 +106,9 @@ class RootController(controller.JSAppLauncher, UsesAnnotations):
|
||||
if "authz_method" in kwd:
|
||||
authz_method = kwd["authz_method"]
|
||||
if data:
|
||||
current_user_roles = trans.get_current_user_roles()
|
||||
if authz_method == "rbac" and trans.app.security_agent.can_access_dataset(current_user_roles, data):
|
||||
if authz_method == "rbac" and trans.app.security_agent.can_access_dataset(
|
||||
trans.get_current_user_roles(), data.dataset
|
||||
):
|
||||
trans.response.set_content_type(data.get_mime())
|
||||
trans.log_event(f"Formatted dataset id {str(id)} for display at {display_app}")
|
||||
return data.as_display_type(display_app, **kwd)
|
||||
|
||||
@@ -82,14 +82,14 @@ class JobsApiTestCase(ApiTestCase, TestsTools):
|
||||
|
||||
@uses_test_history(require_new=True)
|
||||
def test_index_date_filter(self, history_id):
|
||||
self.__history_with_new_dataset(history_id)
|
||||
two_weeks_ago = (datetime.datetime.utcnow() - datetime.timedelta(14)).isoformat()
|
||||
last_week = (datetime.datetime.utcnow() - datetime.timedelta(7)).isoformat()
|
||||
next_week = (datetime.datetime.utcnow() + datetime.timedelta(7)).isoformat()
|
||||
today = datetime.datetime.utcnow().isoformat()
|
||||
tomorrow = (datetime.datetime.utcnow() + datetime.timedelta(1)).isoformat()
|
||||
self.__history_with_new_dataset(history_id)
|
||||
|
||||
jobs = self.__jobs_index(data={"date_range_min": today[0:10], "date_range_max": tomorrow[0:10]})
|
||||
jobs = self.__jobs_index(data={"date_range_min": today, "date_range_max": tomorrow})
|
||||
assert len(jobs) > 0
|
||||
today_job_id = jobs[0]["id"]
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
|
||||
from galaxy.util.json import (
|
||||
safe_dumps,
|
||||
swap_inf_nan,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"val,expected_val",
|
||||
[
|
||||
(float("inf"), "__Infinity__"),
|
||||
(float("-inf"), "__-Infinity__"),
|
||||
(float("NaN"), "__NaN__"),
|
||||
(Decimal("1"), "1"),
|
||||
],
|
||||
)
|
||||
def test_swap_inf_nan(val, expected_val):
|
||||
assert swap_inf_nan(val) == expected_val
|
||||
|
||||
|
||||
def test_safe_dumps():
|
||||
assert safe_dumps({"a": Decimal("0.1")}) == """{"a": "0.1"}"""
|
||||
Reference in New Issue
Block a user