Skip to content

Commit

Permalink
Object Store creation templates.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmchilton committed Apr 1, 2023
1 parent 1e27fe4 commit f03f580
Show file tree
Hide file tree
Showing 16 changed files with 725 additions and 0 deletions.
54 changes: 54 additions & 0 deletions client/src/components/ObjectStore/Templates/CreateForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script lang="ts" setup>
import { computed } from "vue";
import type { ObjectStoreTemplateSummary } from "../services";
import FormCard from "@/components/Form/FormCard.vue";
import FormDisplay from "@/components/Form/FormDisplay.vue";
interface CreateFormProps {
template: ObjectStoreTemplateSummary;
}
const props = defineProps<CreateFormProps>();
const title = "Create new object store for your data";
const submitTitle = "Submit";
const inputs = computed(() => {
const form = [];
const variables = props.template.variables ?? [];
const secrets = props.template.secrets ?? [];
for(const variable of variables) {
// TODO: markdown on help
form.push({
name: variable.name,
type: "text",
help: variable.help,
});
}
for(const secret of secrets) {
// TODO: markdown on help
form.push({
name: secret.name,
type: "password",
help: secret.help,
});
}
return form;
});
async function onSubmit() {
console.log("Submitted!");
}
</script>
<template>
<div>
<FormCard :title="title">
<template v-slot:body>
<FormDisplay :inputs="inputs" />
</template>
</FormCard>
<div class="mt-3">
<b-button id="submit" variant="primary" class="mr-1" @click="onSubmit()">
{{ submitTitle }}
</b-button>
</div>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<script lang="ts" setup>
import { computed, ref, onMounted } from "vue";
import SelectTemplate from "./SelectTemplate.vue";
import CreateForm from "./CreateForm.vue";
import { type ObjectStoreTemplateSummaries, type ObjectStoreTemplateSummary, getTemplates } from "../services";
import LoadingSpan from "@/components/LoadingSpan.vue";
import { errorMessageAsString } from "@/utils/simple-error";
const loadingTemplatesInfoMessage = "Loading object store templates";
const templateId = ref<string | null>(null);
const templates = ref<ObjectStoreTemplateSummaries>([]);
const currentTemplate = computed<ObjectStoreTemplateSummary | null>(() => {
const tId = templateId.value;
if(tId) {
for(const template of templates.value) {
if (template.id == tId) {
return template;
}
}
}
return null;
});
const loading = ref(true);
const error = ref<string | null>(null);
function handleError(e: unknown) {
const errorMessage = errorMessageAsString(e);
error.value = errorMessage;
}
onMounted(async () => {
try {
const data = await getTemplates();
templates.value = data;
loading.value = false;
} catch (e) {
handleError(e);
}
});
async function chooseTemplate(selectTemplateId: string) {
templateId.value = selectTemplateId;
}
</script>
<template>
<b-container fluid class="p-0">
<loading-span v-if="loading" :message="loadingTemplatesInfoMessage" />
<div v-else>
<b-alert v-if="error" variant="danger" class="object-store-selection-error" show>
{{ error }}
</b-alert>

<div v-if="currentTemplate == null">
<select-template :templates="templates" @onSubmit="chooseTemplate" />
</div>

<div v-else>
<create-form :template="currentTemplate" />
</div>

</div>
</b-container>
</template>
52 changes: 52 additions & 0 deletions client/src/components/ObjectStore/Templates/SelectTemplate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<script lang="ts" setup>
import type { ObjectStoreTemplateSummaries } from "../services";
interface SelectTemplateProps {
templates: ObjectStoreTemplateSummaries;
}
withDefaults(defineProps<SelectTemplateProps>(), {});
const popoverPlacement = "rightbottom";
const emit = defineEmits<{
(e: "onSubmit", id: string): void;
}>();
async function handleSubmit(templateId: string) {
emit("onSubmit", templateId);
}
</script>

<template>
<div>
<b-row>
<b-col cols="7">
<b-button-group vertical size="lg" style="width: 100%">
<b-button
v-for="template in templates"
:id="`object-store-template-button-${template.id}`"
:key="template.id"
class="object-store-template-select-button"
:data-template-id="template.id"
@click="handleSubmit(template.id)"
>{{ template.name }}
</b-button>
</b-button-group>
</b-col>
<b-col cols="5">
<p v-localize style="float: right">Some cool text will go here or maybe it will be bigger!</p>
</b-col>
</b-row>
<b-popover
v-for="template in templates"
:key="template.id"
:target="`object-store-template-button-${template.id}`"
triggers="hover"
boundary="window"
:placement="popoverPlacement">
<template v-slot:title>{{ template.name }}</template>
TODO: put interesting information in this popup.
</b-popover>
</div>
</template>
10 changes: 10 additions & 0 deletions client/src/components/ObjectStore/services.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import type { components } from "@/schema";
import { fetcher } from "@/schema/fetcher";

const getObjectStores = fetcher.path("/api/object_stores").method("get").create();
const getObjectStoreTemplates = fetcher.path("/api/object_store_templates").method("get").create();

export type ObjectStoreTemplateSummaries = components["schemas"]["ObjectStoreTemplateSummaries"];
export type ObjectStoreTemplateSummary = components["schemas"]["ObjectStoreTemplateSummary"];

export async function getSelectableObjectStores() {
const { data } = await getObjectStores({ selectable: true });
return data;
}

export async function getTemplates(): Promise<ObjectStoreTemplateSummaries> {
const { data } = await getObjectStoreTemplates({});
return data;
}
5 changes: 5 additions & 0 deletions client/src/entry/analysis/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import VisualizationPublished from "components/Visualizations/VisualizationPubli
import WorkflowImport from "components/Workflow/WorkflowImport";
import WorkflowList from "components/Workflow/WorkflowList";
import WorkflowPublished from "components/Workflow/WorkflowPublished";
import CreateUserObjectStore from "components/ObjectStore/Templates/CreateUserObjectStore";
import { APIKey } from "components/User/APIKey";
import { CloudAuth } from "components/User/CloudAuth";
import { ExternalIdentities } from "components/User/ExternalIdentities";
Expand Down Expand Up @@ -271,6 +272,10 @@ export function getRouter(Galaxy) {
termsUrl: Galaxy.config.terms_url,
},
},
{
path: "object_store/create",
component: CreateUserObjectStore,
},
{
path: "pages/create",
component: FormGeneric,
Expand Down
5 changes: 5 additions & 0 deletions lib/galaxy/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
BaseObjectStore,
build_object_store_from_config,
)
from galaxy.objectstore.templates import ConfiguredObjectStoreTemplates
from galaxy.queue_worker import (
GalaxyQueueWorker,
send_local_control_task,
Expand Down Expand Up @@ -549,6 +550,10 @@ def __init__(self, configure_logging=True, use_converters=True, use_display_appl
ConfiguredFileSources, ConfiguredFileSources.from_app_config(self.config)
)

self.object_store_templates = self._register_singleton(
ConfiguredObjectStoreTemplates, ConfiguredObjectStoreTemplates.from_app_config(self.config)
)

self.vault = self._register_singleton(Vault, VaultFactory.from_app(self)) # type: ignore[type-abstract]
# Load security policy.
self.security_agent = self.model.security_agent
Expand Down
15 changes: 15 additions & 0 deletions lib/galaxy/config/schemas/config_schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,21 @@ mapping:
desc: |
FileSource plugins described embedded into Galaxy's config.
object_store_templates_config_file:
type: str
default: object_store_templates.yml
path_resolves_to: config_dir
required: false
desc: |
Configured Object Store templates configuration file.
object_store_templates:
type: seq
sequence:
- type: any
desc: |
Configured Object Store templates embedded into Galaxy's config.
enable_mulled_containers:
type: bool
default: true
Expand Down
17 changes: 17 additions & 0 deletions lib/galaxy/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10065,6 +10065,23 @@ def __init__(self, name=None, value=None):
self.value = value


class UserObjectStore(Base, RepresentById):
__tablename__ = "user_object_store"

id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey("galaxy_user.id"), index=True)
create_time = Column(DateTime, default=now)
update_time = Column(DateTime, default=now, onupdate=now, index=True)
name = Column(String(255), index=True)
object_store_template_id = Column(String(255), index=True)
object_store_template_version = Column(Integer, index=True)
# Maybe just use the id/version and don't record the definition
object_store_template_definition = Column(JSONType)
# Maybe convert these maps to association tables encoding maps...
object_store_template_variables = Column(JSONType)
object_store_template_secrets = Column(JSONType)


class UserAction(Base, RepresentById):
__tablename__ = "user_action"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""add user defined object stores
Revision ID: c14a3c93d66a
Revises: 460d0ecd1dd8
Create Date: 2023-04-01 17:25:37.553039
"""
import sqlalchemy as sa
from alembic import op
from sqlalchemy import (
Column,
DateTime,
ForeignKey,
Integer,
String,
)

from galaxy.model.custom_types import JSONType

# revision identifiers, used by Alembic.
revision = "c14a3c93d66a"
down_revision = "460d0ecd1dd8"
branch_labels = None
depends_on = None


# database object names used in this revision
table_name = "user_object_store"


def upgrade():
op.create_table(
table_name,
Column("id", Integer, primary_key=True),
Column("user_id", Integer, ForeignKey("galaxy_user.id"), nullable=False),
Column("name", String(255), index=True),
Column("create_time", DateTime),
Column("update_time", DateTime),
Column("template_id", String(255), index=True),
Column("template_version", Integer, index=True),
# Maybe just use the id/version and don't record the definition
Column("template_definition", JSONType),
# Maybe convert these maps to association tables encoding maps...
Column("template_variables", JSONType),
Column("template_secrets", JSONType),
)


def downgrade():
op.drop_table(table_name)
7 changes: 7 additions & 0 deletions lib/galaxy/objectstore/templates/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .manager import ConfiguredObjectStoreTemplates
from .models import ObjectStoreTemplateSummaries

__all__ = (
"ConfiguredObjectStoreTemplates",
"ObjectStoreTemplateSummaries",
)
Loading

0 comments on commit f03f580

Please sign in to comment.