forked from galaxyproject/galaxy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
458 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { fetcher } from "@/api/schema"; | ||
|
||
export const sharing = fetcher.path("/api/workflows/{id}/sharing").method("get").create(); | ||
export const enableLink = fetcher.path("/api/workflows/{id}/enable_link_access").method("put").create(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,267 @@ | ||
<script setup lang="ts"> | ||
import axios from "axios"; | ||
import Vue, { computed, Ref, ref, watch } from "vue"; | ||
import { enableLink, sharing } from "@/api/histories"; | ||
import { enableLink as enableLinkWorkflow, sharing as sharingWorkflow } from "@/api/workflows"; | ||
import { referencedObjects } from "@/components/Markdown/parse"; | ||
import { useDatasetStore } from "@/stores/datasetStore"; | ||
import { useHistoryStore } from "@/stores/historyStore"; | ||
import { useWorkflowStore } from "@/stores/workflowStore"; | ||
import _l from "@/utils/localization"; | ||
import { withPrefix } from "@/utils/redirect"; | ||
import PermissionObjectType from "./PermissionObjectType.vue"; | ||
import SharingIndicator from "./SharingIndicator.vue"; | ||
import LoadingSpan from "@/components/LoadingSpan.vue"; | ||
const { getHistoryNameById, loadHistoryById } = useHistoryStore(); | ||
const { getStoredWorkflowNameByInstanceId, fetchWorkflowForInstanceId } = useWorkflowStore(); | ||
const { getDataset, fetchDataset } = useDatasetStore(); | ||
interface ObjectPermissionsProps { | ||
markdownContent: string; | ||
} | ||
const props = defineProps<ObjectPermissionsProps>(); | ||
const referencedJobIds = ref<string[]>([]); | ||
const referencedHistoryDatasetIds = ref<string[]>([]); | ||
const referencedHistoryDatasetCollectionIds = ref<string[]>([]); | ||
const referencedWorkflowIds = ref<string[]>([]); | ||
const referencedInvocationIds = ref<string[]>([]); | ||
// We mostly defer to history permissions for all these objects. Track them... | ||
const jobsToHistories: Ref<{ [key: string]: string }> = ref({}); | ||
const invocationsToHistories: Ref<{ [key: string]: string }> = ref({}); | ||
const historyDatasetCollectionsToHistories: Ref<{ [key: string]: string }> = ref({}); | ||
const historyAccessible: Ref<{ [key: string]: Boolean | null }> = ref({}); | ||
const workflowAccessible: Ref<{ [key: string]: Boolean | null }> = ref({}); | ||
const historyDatasetAccessible: Ref<{ [key: string]: Boolean | null }> = ref({}); | ||
watch(referencedJobIds, async () => { | ||
referencedJobIds.value.forEach((jobId) => { | ||
if (jobId in jobsToHistories.value) { | ||
return; | ||
} | ||
axios.get(withPrefix(`/api/jobs/${jobId}`)).then((response) => { | ||
const historyId = response.data.history_id; | ||
Vue.set(jobsToHistories.value, jobId, historyId); | ||
}); | ||
}); | ||
}); | ||
watch(referencedInvocationIds, async () => { | ||
referencedInvocationIds.value.forEach((invocationId) => { | ||
if (invocationId in invocationsToHistories.value) { | ||
return; | ||
} | ||
axios.get(withPrefix(`/api/invocations/${invocationId}`)).then((response) => { | ||
const historyId = response.data.history_id; | ||
console.log(`${invocationId} => ${historyId}`); | ||
Vue.set(invocationsToHistories.value, invocationId, historyId); | ||
}); | ||
}); | ||
}); | ||
watch(referencedHistoryDatasetCollectionIds, async () => { | ||
referencedHistoryDatasetCollectionIds.value.forEach((historyDatasetCollectionId) => { | ||
if (historyDatasetCollectionId in historyDatasetCollectionsToHistories.value) { | ||
return; | ||
} | ||
axios.get(withPrefix(`/api/invocations/${historyDatasetCollectionId}`)).then((response) => { | ||
const historyId = response.data.history_id; | ||
console.log(`${historyDatasetCollectionId} => ${historyId}`); | ||
Vue.set(historyDatasetCollectionsToHistories.value, historyDatasetCollectionId, historyId); | ||
}); | ||
}); | ||
}); | ||
const historyIds = computed<string[]>(() => { | ||
// be sure to reference all refs required for full computation | ||
const jobIds = referencedJobIds.value; | ||
const jobMapping = jobsToHistories.value; | ||
const invocationIds = referencedInvocationIds.value; | ||
const invocationMapping = invocationsToHistories.value; | ||
const collectionIds = referencedHistoryDatasetCollectionIds.value; | ||
const collectionMapping = historyDatasetCollectionsToHistories.value; | ||
const theHistories = new Set(); | ||
for (const jobId of jobIds) { | ||
if (jobId in jobMapping) { | ||
theHistories.add(jobMapping[jobId]); | ||
} | ||
} | ||
for (const invocationId of invocationIds) { | ||
if (invocationId in invocationMapping) { | ||
theHistories.add(invocationMapping[invocationId]); | ||
} | ||
} | ||
for (const historyDatasetCollectionId of collectionIds) { | ||
if (historyDatasetCollectionId in collectionMapping) { | ||
theHistories.add(collectionMapping[historyDatasetCollectionId]); | ||
} | ||
} | ||
const historyIds = Array.from(theHistories.values()) as string[]; | ||
return historyIds; | ||
}); | ||
interface ItemInterface { | ||
id: string; | ||
accessible: Boolean | null; | ||
name: string; | ||
type: string; | ||
} | ||
const histories = computed<ItemInterface[]>(() => { | ||
return historyIds.value.map((historyId: string) => { | ||
return { | ||
id: historyId, | ||
type: "history", | ||
name: getHistoryNameById(historyId), | ||
accessible: historyAccessible.value[historyId], | ||
} as ItemInterface; | ||
}); | ||
}); | ||
const workflows = computed<ItemInterface[]>(() => { | ||
return referencedWorkflowIds.value.map((workflowId: string) => { | ||
return { | ||
id: workflowId, | ||
type: "workflow", | ||
name: getStoredWorkflowNameByInstanceId(workflowId), | ||
accessible: workflowAccessible.value[workflowId], | ||
} as ItemInterface; | ||
}); | ||
}); | ||
const datasets = computed<ItemInterface[]>(() => { | ||
return referencedHistoryDatasetIds.value.map((historyDatasetId: string) => { | ||
return { | ||
id: historyDatasetId, | ||
type: "historyDataset", | ||
name: getDataset(historyDatasetId)?.name || "Fetching dataset name...", | ||
accessible: historyDatasetAccessible.value[historyDatasetId], | ||
} as ItemInterface; | ||
}); | ||
}); | ||
const loading = ref(false); | ||
const SHARING_FIELD = { key: "accessible", label: _l("Accessible"), sortable: false, thStyle: { width: "10%" } }; | ||
const NAME_FIELD = { key: "name", label: _l("Name"), sortable: true }; | ||
const TYPE_FIELD = { key: "type", label: _l("Type"), sortable: true, thStyle: { width: "10%" } }; | ||
const tableFields = [SHARING_FIELD, TYPE_FIELD, NAME_FIELD]; | ||
watch( | ||
props, | ||
() => { | ||
const objects = referencedObjects(props.markdownContent); | ||
referencedJobIds.value = Array.from(objects.jobs.values()); | ||
referencedHistoryDatasetIds.value = Array.from(objects.historyDatasets.values()); | ||
referencedHistoryDatasetCollectionIds.value = Array.from(objects.historyDatasetCollections.values()); | ||
referencedWorkflowIds.value = Array.from(objects.workflows.values()); | ||
referencedInvocationIds.value = Array.from(objects.invocations.values()); | ||
initWorkflowData(); | ||
initHistoryDatasetData(); | ||
}, | ||
{ immediate: true } | ||
); | ||
watch(historyIds, () => { | ||
for (const historyId of historyIds.value) { | ||
loadHistoryById(historyId); | ||
if (historyId && !(historyId in historyAccessible.value)) { | ||
Vue.set(historyAccessible.value, historyId, null); | ||
sharing({ history_id: historyId }).then((response) => { | ||
const accessible = response.data.importable; | ||
Vue.set(historyAccessible.value, historyId, accessible); | ||
}); | ||
} | ||
} | ||
}); | ||
function initWorkflowData() { | ||
for (const workflowId of referencedWorkflowIds.value) { | ||
console.log(`workflow id is ${workflowId}`); | ||
fetchWorkflowForInstanceId(workflowId); | ||
if (workflowId && !(workflowId in workflowAccessible.value)) { | ||
Vue.set(workflowAccessible.value, workflowId, null); | ||
sharingWorkflow({ id: workflowId }).then((response) => { | ||
const accessible = response.data.importable; | ||
Vue.set(workflowAccessible.value, workflowId, accessible); | ||
}); | ||
} | ||
} | ||
} | ||
function initHistoryDatasetData() { | ||
for (const historyDatasetId of referencedHistoryDatasetIds.value) { | ||
fetchDataset({ id: historyDatasetId }); | ||
if (historyDatasetId && !(historyDatasetId in historyDatasetAccessible.value)) { | ||
axios.get(withPrefix(`/dataset/get_edit?dataset_id=${historyDatasetId}`)).then((response) => { | ||
const permissionInputs = response.data.permission_inputs; | ||
const accessPermissionInput = permissionInputs[1]; | ||
if (accessPermissionInput.name != "DATASET_ACCESS") { | ||
throw Error("Galaxy Bug"); | ||
} | ||
const accessible = (accessPermissionInput.value || []).length == 0; | ||
Vue.set(historyDatasetAccessible.value, historyDatasetId, accessible); | ||
}); | ||
} | ||
} | ||
} | ||
const tableItems = computed<ItemInterface[]>(() => { | ||
return [...histories.value, ...workflows.value, ...datasets.value]; | ||
}); | ||
function makeAccessible(item: ItemInterface) { | ||
if (item.type == "history") { | ||
enableLink({ history_id: item.id }).then((response) => { | ||
Vue.set(historyAccessible.value, item.id, true); | ||
}); | ||
} else if (item.type == "workflow") { | ||
enableLinkWorkflow({ id: item.id }).then((response) => { | ||
Vue.set(workflowAccessible.value, item.id, true); | ||
}); | ||
} else if (item.type == "historyDataset") { | ||
const data = { | ||
dataset_id: item.id, | ||
action: "remove_restrictions", | ||
}; | ||
axios.put(withPrefix(`/api/datasets/${item.id}/permissions`), data).then((response) => { | ||
console.log(response); | ||
Vue.set(historyDatasetAccessible.value, item.id, true); | ||
}); | ||
} | ||
} | ||
</script> | ||
|
||
<template> | ||
<div> | ||
<b-table :items="tableItems" :fields="tableFields"> | ||
<template v-slot:empty> | ||
<LoadingSpan v-if="loading" message="Loading objects" /> | ||
<b-alert v-else variant="info" show> | ||
<div>No objects found in referenced Galaxy markdown content.</div> | ||
</b-alert> | ||
</template> | ||
<template v-slot:cell(name)="row"> | ||
{{ row.item.name }} | ||
</template> | ||
<template v-slot:cell(accessible)="row"> | ||
<SharingIndicator :accessible="row.item.accessible" @makeAccessible="makeAccessible(row.item)" /> | ||
</template> | ||
<template v-slot:cell(type)="row"> | ||
<PermissionObjectType :type="row.item.type" /> | ||
</template> | ||
</b-table> | ||
</div> | ||
</template> |
15 changes: 15 additions & 0 deletions
15
client/src/components/PageEditor/ObjectPermissionsModal.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<script setup lang="ts"> | ||
import ObjectPermissions from "./ObjectPermissions.vue"; | ||
interface ObjectPermissionsProps { | ||
markdownContent: string; | ||
} | ||
defineProps<ObjectPermissionsProps>(); | ||
</script> | ||
|
||
<template> | ||
<b-modal v-bind="$attrs" title="Page Object Permissions" title-tag="h2" ok-only v-on="$listeners"> | ||
<ObjectPermissions :markdown-content="markdownContent" /> | ||
</b-modal> | ||
</template> |
Oops, something went wrong.