Merge branch 'release_23.1' into dev

This commit is contained in:
mvdbeek
2023-10-06 16:17:22 -04:00
10 changed files with 86 additions and 23 deletions
@@ -66,6 +66,7 @@ describe("FormSelect", () => {
it("multiple values", async () => {
const wrapper = createTarget({
optional: true,
multiple: true,
optional: true,
options: defaultOptions,
@@ -13,13 +13,14 @@ import HistoryView from "./HistoryView";
const localVue = getLocalVue();
jest.mock("stores/services/history.services");
function create_history(historyId, userId, purged = false) {
function create_history(historyId, userId, purged = false, archived = false) {
const historyName = `${userId}'s History ${historyId}`;
return {
model_class: "History",
id: historyId,
name: historyName,
purged: purged,
archived: archived,
count: 10,
annotation: "This is a history",
tags: ["tag_1", "tag_2"],
@@ -164,13 +165,43 @@ describe("History center panel View", () => {
const wrapper = await createWrapper(localVue, "user_1", history);
expect(wrapper.vm.history).toEqual(history);
// switch/import buttons: purged they don't exist
// history purged, not switchable and not importable
const switchButton = wrapper.find("[data-description='switch to history button']");
const importButton = wrapper.find("[data-description='import history button']");
expect(switchButton.exists()).toBe(false);
expect(switchButton.attributes("disabled")).toBeTruthy();
expect(importButton.exists()).toBe(false);
// instead we have an alert
expect(wrapper.find("[data-description='history state info']").text()).toBe("This history has been purged.");
});
it("should not display archived message and should be importable when user is not owner and history is archived", async () => {
const history = create_history("history_2", "user_2", false, true);
const wrapper = await createWrapper(localVue, "user_1", history);
expect(wrapper.vm.history).toEqual(history);
const switchButton = wrapper.find("[data-description='switch to history button']");
const importButton = wrapper.find("[data-description='import history button']");
expect(switchButton.exists()).toBe(false);
expect(importButton.exists()).toBe(true);
expect(importButton.attributes("disabled")).toBeFalsy();
expectCorrectLayout(wrapper);
expect(wrapper.find("[data-description='history state info']").exists()).toBe(false);
});
it("should display archived message and should not be importable when user is owner and history is archived", async () => {
const history = create_history("history_2", "user_1", false, true);
const wrapper = await createWrapper(localVue, "user_1", history);
expect(wrapper.vm.history).toEqual(history);
const switchButton = wrapper.find("[data-description='switch to history button']");
const importButton = wrapper.find("[data-description='import history button']");
expect(switchButton.exists()).toBe(true);
expect(switchButton.attributes("disabled")).toBeTruthy();
expect(importButton.exists()).toBe(false);
expectCorrectLayout(wrapper);
expect(wrapper.find("[data-description='history state info']").text()).toBe("This history has been archived.");
});
});
+28 -7
View File
@@ -3,19 +3,19 @@
<b-alert v-if="showHistoryStateInfo" variant="info" show data-description="history state info">
{{ historyStateInfoMessage }}
</b-alert>
<div v-else class="flex-row flex-grow-0 pb-3">
<div class="flex-row flex-grow-0 pb-3">
<b-button
v-if="userOwnsHistory"
size="sm"
variant="outline-info"
title="Switch to this history"
:title="setAsCurrentTitle"
:disabled="isSetAsCurrentDisabled"
data-description="switch to history button"
@click="setCurrentHistory(history.id)">
Switch to this history
</b-button>
<b-button
v-else
v-if="canImportHistory"
v-b-modal:copy-history-modal
size="sm"
variant="outline-info"
@@ -77,25 +77,46 @@ export default {
userOwnsHistory() {
return this.currentUser.id == this.history.user_id;
},
isCurrentHistory() {
return this.currentHistory?.id == this.history?.id;
},
isSetAsCurrentDisabled() {
return this.currentHistory?.id == this.history?.id || this.history.archived || this.history.purged;
return this.isCurrentHistory || this.history.archived || this.history.purged;
},
setAsCurrentTitle() {
if (this.isCurrentHistory) {
return "This history is already your current history.";
}
if (this.history.archived) {
return "This history has been archived and cannot be set as your current history. Unarchive it first.";
}
if (this.history.purged) {
return "This history has been purged and cannot be set as your current history.";
}
return "Switch to this history";
},
canEditHistory() {
return this.userOwnsHistory && !this.history.archived && !this.history.purged;
},
showHistoryArchived() {
return this.history.archived && this.userOwnsHistory;
},
showHistoryStateInfo() {
return this.history.archived || this.history.purged;
return this.showHistoryArchived || this.history.purged;
},
historyStateInfoMessage() {
if (this.history.archived && this.history.purged) {
if (this.showHistoryArchived && this.history.purged) {
return "This history has been archived and purged.";
} else if (this.history.archived) {
} else if (this.showHistoryArchived) {
return "This history has been archived.";
} else if (this.history.purged) {
return "This history has been purged.";
}
return "";
},
canImportHistory() {
return !this.userOwnsHistory && !this.history.purged;
},
},
created() {
this.loadHistoryById(this.id);
@@ -62,7 +62,7 @@ export default {
};
},
computed: {
...mapState(useUserStore, ["isAnonymous"]),
...mapState(useUserStore, ["currentUser", "isAnonymous"]),
title() {
return `Copying History: ${this.history.name}`;
},
@@ -72,9 +72,12 @@ export default {
saveVariant() {
return this.loading ? "info" : this.formValid ? "primary" : "secondary";
},
userOwnsHistory() {
return this.currentUser.id == this.history.user_id;
},
newNameValid() {
if (this.name == this.history.name) {
return null;
if (this.userOwnsHistory && this.name == this.history.name) {
return false;
}
return this.name.length > 0;
},
@@ -149,6 +149,9 @@ const infoString = computed(() => {
invocationMessage.workflow_step_id + 1
} is a conditional step and the result of the when expression is not a boolean type.`;
} else if (reason === "unexpected_failure") {
if (invocationMessage.details) {
return `${failFragment} an unexpected failure occurred: '${invocationMessage.details}'`;
}
return `${failFragment} an unexpected failure occurred.`;
} else if (reason === "workflow_output_not_found") {
return `Defined workflow output '${invocationMessage.output_name}' was not found in step ${
+1 -1
View File
@@ -47,7 +47,7 @@ for _tag in reversed(tags):
if Version(_ver) >= MIN_DOC_VERSION:
simpleversioning_versions.append({"id": f"release_{_ver}", "name": _ver})
if re.fullmatch(r"release_\d{2}\.\d{2}", TARGET_GIT_BRANCH):
if re.fullmatch(r"release_\d{2}\.\d{1,2}", TARGET_GIT_BRANCH):
if _stable:
# The current stable release will go here but fail the next conditional, avoiding either banner.
if TARGET_GIT_BRANCH != f"release_{_stable}":
+1 -1
View File
@@ -498,7 +498,7 @@ class RemoveTagDatasetAction(TagDatasetAction):
@classmethod
def _execute(cls, tag_handler, user, output, tags):
tag_handler.remove_tags_from_list(user, output, tags)
tag_handler.remove_tags_from_list(user, output, tags, flush=False)
class ActionBox:
+10 -4
View File
@@ -95,7 +95,8 @@ class ToolBoxRegistryImpl(ToolBoxRegistry):
self.__toolbox = toolbox
def has_tool(self, tool_id: str) -> bool:
return tool_id in self.__toolbox._tools_by_id
toolbox = self.__toolbox
return tool_id in toolbox._tools_by_id or tool_id in toolbox._tools_by_old_id
def get_tool(self, tool_id: str):
return self.__toolbox.get_tool(tool_id)
@@ -161,6 +162,7 @@ class AbstractToolBox(Dictifiable, ManagesIntegratedToolPanelMixin):
# so each will be present once in the above dictionary. The following
# dictionary can instead hold multiple tools with different versions.
self._tool_versions_by_id = {}
self._tools_by_old_id = {}
self._workflows_by_id = {}
# Cache for tool's to_dict calls specific to toolbox. Invalidates on toolbox reload.
self._tool_to_dict_cache = {}
@@ -718,9 +720,8 @@ class AbstractToolBox(Dictifiable, ManagesIntegratedToolPanelMixin):
rval.append(lineage_tool)
if not rval:
# still no tool, do a deeper search and try to match by old ids
for tool in self._tools_by_id.values():
if tool.old_id == tool_id:
rval.append(tool)
if tool_id in self._tools_by_old_id:
rval.extend(self._tools_by_old_id[tool_id])
if get_all_versions and tool_id in self._tool_versions_by_id:
for tool in self._tool_versions_by_id[tool_id].values():
if tool not in rval:
@@ -1160,6 +1161,10 @@ class AbstractToolBox(Dictifiable, ManagesIntegratedToolPanelMixin):
self._tools_by_id[tool_id] = tool
else:
self._tools_by_id[tool_id] = tool
old_id = tool.old_id
if old_id not in self._tools_by_old_id:
self._tools_by_old_id[old_id] = []
self._tools_by_old_id[old_id].append(tool)
def package_tool(self, trans, tool_id):
"""
@@ -1219,6 +1224,7 @@ class AbstractToolBox(Dictifiable, ManagesIntegratedToolPanelMixin):
else:
tool = self._tools_by_id[tool_id]
del self._tools_by_id[tool_id]
self._tools_by_old_id[tool.old_id].remove(tool)
tool_cache = getattr(self.app, "tool_cache", None)
if tool_cache:
tool_cache.expire_tool(tool_id)
+1 -1
View File
@@ -2,7 +2,7 @@ name: Custom Panel in a New Section
type: generic
items:
- type: section
name: My Completely New Sectin
name: My Completely New Section
items:
- type: label
text: The Start
@@ -125,9 +125,7 @@ class TestRepositoryInstallIntegrationTestCase(integration_util.IntegrationTestC
assert tool["version"] == "0.0.2"
self.uninstall_repository(REPO.owner, REPO.name, REPO.changeset)
response = self.get_tool(assert_ok=False)
assert (
"err_msg" in response
), f"Expected an error message after tool install but response was {response.content}"
assert "err_msg" in response, f"Expected an error message after tool install but response was {response}"
assert response["err_msg"]
assert self.get_installed_repository_for(REPO.owner, REPO.name, REPO.changeset) is None