mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Fix history store handling, avoid overwriting loaded attributes
This commit is contained in:
@@ -1,17 +1,30 @@
|
||||
<template>
|
||||
<CurrentUser v-slot="{ user }">
|
||||
<UserHistories v-if="user" v-slot="{ handlers }" :user="user">
|
||||
<div v-if="getHistory" class="d-flex flex-column h-100">
|
||||
<div v-if="history" class="d-flex flex-column h-100">
|
||||
<div class="flex-row flex-grow-0">
|
||||
<b-button
|
||||
v-if="user.id == history.user_id"
|
||||
size="sm"
|
||||
variant="outline-info"
|
||||
title="Switch to this history"
|
||||
@click="handlers.setCurrentHistory(history)">
|
||||
Switch to this history
|
||||
</b-button>
|
||||
<b-button v-else size="sm" variant="outline-info" title="Import this history">
|
||||
Import this history
|
||||
</b-button>
|
||||
</div>
|
||||
<CollectionPanel
|
||||
v-if="selectedCollections.length && selectedCollections[0].history_id == id"
|
||||
:history="getHistory"
|
||||
:history="history"
|
||||
:selected-collections.sync="selectedCollections"
|
||||
:show-controls="false"
|
||||
@view-collection="onViewCollection" />
|
||||
<HistoryPanel
|
||||
v-else
|
||||
v-on="handlers"
|
||||
:history="getHistory"
|
||||
:history="history"
|
||||
:show-controls="false"
|
||||
@view-collection="onViewCollection" />
|
||||
</div>
|
||||
@@ -49,9 +62,12 @@ export default {
|
||||
selectedCollections: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch("history/loadHistoryById", this.id);
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({ getHistoryById: "history/getHistoryById" }),
|
||||
getHistory() {
|
||||
history() {
|
||||
return this.getHistoryById(this.id);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -32,14 +32,13 @@ const mutations = {
|
||||
Vue.delete(state.histories, doomed.id);
|
||||
},
|
||||
setHistories(state, newHistories = []) {
|
||||
const currentHistoryId = state.currentHistoryId;
|
||||
const currentHistory = state.histories[currentHistoryId];
|
||||
const newMap = newHistories.reduce((acc, h) => ({ ...acc, [h.id]: h }), {});
|
||||
if (currentHistory) {
|
||||
// The incoming history list contains less information than the current history
|
||||
// so we restore the existing current history since it gets updated regularly anyway
|
||||
newMap[currentHistoryId] = currentHistory;
|
||||
}
|
||||
// The incoming history list contains less information than the current history
|
||||
// so we ensure that already available details are not getting lost
|
||||
const enrichedHistories = newHistories.map((history) => {
|
||||
const historyState = state.histories[history.id] || {};
|
||||
return Object.assign({}, history, historyState);
|
||||
});
|
||||
const newMap = enrichedHistories.reduce((acc, h) => ({ ...acc, [h.id]: h }), {});
|
||||
Vue.set(state, "histories", newMap);
|
||||
},
|
||||
setHistoriesLoading(state, isLoading) {
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import { scrubModelProps } from "utils/safeAssign";
|
||||
|
||||
export class History {
|
||||
constructor(doc = {}) {
|
||||
try {
|
||||
this.loadProps(doc);
|
||||
} catch (err) {
|
||||
console.debug("Unable to load history props.", doc);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
loadProps(raw = {}) {
|
||||
Object.assign(this, raw);
|
||||
}
|
||||
|
||||
clone() {
|
||||
const newProps = cleanHistoryProps(this);
|
||||
return new History(newProps);
|
||||
}
|
||||
|
||||
patch(newProps) {
|
||||
const cleanProps = cleanHistoryProps({ ...this, ...newProps });
|
||||
return new History(cleanProps);
|
||||
}
|
||||
|
||||
equals(other) {
|
||||
return History.equals(this, other);
|
||||
}
|
||||
}
|
||||
|
||||
History.equals = function (a, b) {
|
||||
return JSON.stringify(a) == JSON.stringify(b);
|
||||
};
|
||||
|
||||
const scrubber = scrubModelProps(History);
|
||||
export const cleanHistoryProps = (props = {}) => {
|
||||
const cleanProps = JSON.parse(JSON.stringify(props));
|
||||
return scrubber(cleanProps);
|
||||
};
|
||||
@@ -1,75 +0,0 @@
|
||||
import { History } from "./History";
|
||||
|
||||
describe("History model", () => {
|
||||
const initialProps = {
|
||||
id: "abc123",
|
||||
name: "Foo",
|
||||
tags: ["a", "b", "c"],
|
||||
annotation: "I am a test history",
|
||||
};
|
||||
|
||||
const h = new History(initialProps);
|
||||
|
||||
describe("clone", () => {
|
||||
const c = h.clone();
|
||||
|
||||
it("must be able to clone itself for use in editors", () => {
|
||||
// should be == but not ===
|
||||
expect(h).not.toBe(c);
|
||||
expect(c).toBeInstanceOf(History);
|
||||
expect(History.equals(h, c)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should have the same props before and after cloning", () => {
|
||||
expect(h.id).toEqual(c.id);
|
||||
expect(h.name).toEqual(c.name);
|
||||
expect(h.tags).toEqual(c.tags);
|
||||
expect(h.annotation).toEqual(c.annotation);
|
||||
});
|
||||
});
|
||||
|
||||
describe("patch", () => {
|
||||
const patchVals = {
|
||||
name: "i was patched",
|
||||
annotation: "patched annotation",
|
||||
tags: ["foo", "bar", "blech"],
|
||||
};
|
||||
|
||||
const clone = h.patch(patchVals);
|
||||
|
||||
it("should return a new instance of History", () => {
|
||||
expect(h).not.toBe(clone);
|
||||
expect(clone).toBeInstanceOf(History);
|
||||
expect(History.equals(h, clone)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should patch in passed properties", () => {
|
||||
expect(clone.name).toEqual(patchVals.name);
|
||||
expect(clone.annotation).toEqual(patchVals.annotation);
|
||||
expect(clone.tags).toEqual(patchVals.tags);
|
||||
});
|
||||
});
|
||||
|
||||
describe("equivalence comparison", () => {
|
||||
it("should recognize different props", () => {
|
||||
const hDifferent = new History({ id: "def345" });
|
||||
expect(History.equals(h, hDifferent)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("equivalence should work fine with cloned histories", () => {
|
||||
const clone = h.clone();
|
||||
// different object
|
||||
expect(h).not.toBe(clone);
|
||||
// but equivalent because props the same
|
||||
expect(History.equals(h, clone)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should work with patch", () => {
|
||||
const clone = h.patch({ id: "floobar" });
|
||||
// different object
|
||||
expect(h).not.toBe(clone);
|
||||
// not equivalent because different val
|
||||
expect(History.equals(h, clone)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
import axios from "axios";
|
||||
import { prependPath } from "utils/redirect";
|
||||
import { History } from "./History";
|
||||
|
||||
/**
|
||||
* Generic json getter
|
||||
@@ -47,7 +46,7 @@ const stdHistoryParams = {
|
||||
*/
|
||||
const extendedHistoryParams = {
|
||||
view: "summary",
|
||||
keys: "size,contents_active",
|
||||
keys: "size,contents_active,user_id",
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -56,8 +55,7 @@ const extendedHistoryParams = {
|
||||
export async function getHistoryList() {
|
||||
const url = "api/histories";
|
||||
const response = await axios.get(prependPath(url), { params: stdHistoryParams });
|
||||
const rawList = doResponse(response);
|
||||
return rawList.map((props) => new History(props));
|
||||
return doResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,8 +65,7 @@ export async function getHistoryList() {
|
||||
export async function getHistoryById(id) {
|
||||
const path = `api/histories/${id}`;
|
||||
const response = await axios.get(prependPath(path), { params: extendedHistoryParams });
|
||||
const props = doResponse(response);
|
||||
return new History(props);
|
||||
return doResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,8 +84,7 @@ export async function createNewHistory() {
|
||||
if (!id) {
|
||||
throw new Error("failed to create and select new history");
|
||||
}
|
||||
const newHistoryProps = doResponse(createResponse);
|
||||
return new History(newHistoryProps);
|
||||
return doResponse(createResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,8 +102,7 @@ export async function cloneHistory(history, name, copyAll) {
|
||||
current: true,
|
||||
};
|
||||
const response = await axios.post(prependPath(url), payload, { params: stdHistoryParams });
|
||||
const clonedProps = doResponse(response);
|
||||
return new History(clonedProps);
|
||||
return doResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,8 +124,7 @@ export async function deleteHistoryById(id, purge = false) {
|
||||
export async function updateHistoryFields(id, payload) {
|
||||
const url = `api/histories/${id}`;
|
||||
const response = await axios.put(prependPath(url), payload, { params: extendedHistoryParams });
|
||||
const props = doResponse(response);
|
||||
return new History(props);
|
||||
return doResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,10 +148,7 @@ export async function secureHistory(history) {
|
||||
export async function getCurrentHistoryFromServer(since) {
|
||||
const url = "history/current_history_json";
|
||||
const response = await axios.get(prependPath(url), { params: { since: since } });
|
||||
const props = doResponse(response);
|
||||
if (props) {
|
||||
return new History(props);
|
||||
}
|
||||
return doResponse(response);
|
||||
}
|
||||
|
||||
export async function setCurrentHistoryOnServer(history_id) {
|
||||
|
||||
Reference in New Issue
Block a user