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 24, 2023
1 parent e047311 commit 0cb7333
Show file tree
Hide file tree
Showing 51 changed files with 2,479 additions and 56 deletions.
21 changes: 21 additions & 0 deletions client/src/components/ObjectStore/Instances/EditInstance.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
import { computed } from "vue";
import { useObjectStoreInstancesStore } from "@/stores/objectStoreInstancesStore";
import LoadingSpan from "@/components/LoadingSpan.vue";
const objectStoreInstancesStore = useObjectStoreInstancesStore();
objectStoreInstancesStore.fetchInstances();
interface Props {
instanceId: number;
}
const props = defineProps<Props>();
const instance = computed(() => objectStoreInstancesStore.getInstance(props.instanceId));
</script>
<template>
<div>
<loading-span v-if="!instance" message="Loading object store instance" />
<div v-else>Edit instance {{ instance?.variables }}.</div>
</div>
</template>
54 changes: 54 additions & 0 deletions client/src/components/ObjectStore/Instances/InstanceDropdown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script setup lang="ts">
import { computed } from "vue";
import { useRouter } from "vue-router/composables";
import type { UserConcreteObjectStore } from "./types";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { useObjectStoreTemplatesStore } from "@/stores/objectStoreTemplatesStore";
import "./icons";
const objectStoreTemplatesStore = useObjectStoreTemplatesStore();
const router = useRouter();
// TODO?
const title = "";
interface Props {
objectStore: UserConcreteObjectStore;
}
const props = defineProps<Props>();
const routeEdit = computed(() => `/object_store_instances/${props.objectStore.id}/edit`);
const routeUpgrade = computed(() => `/object_store_instances/${props.objectStore.id}/upgrade`);
const isUpgradable = computed(() =>
objectStoreTemplatesStore.canUpgrade(props.objectStore.template_id, props.objectStore.template_version)
);
</script>

<template>
<div>
<b-link
v-b-tooltip.hover
class="object-store-instance-dropdown font-weight-bold"
data-toggle="dropdown"
:title="title"
aria-haspopup="true"
aria-expanded="false">
<FontAwesomeIcon icon="caret-down" />
<span class="instance-dropdown-name">{{ props.objectStore.name }}</span>
</b-link>
<div class="dropdown-menu" aria-labelledby="object-store-instance-dropdown">
<a
class="dropdown-item"
@keypress="router.push(routeUpgrade)"
@click.prevent="router.push(routeUpgrade)"
v-if="isUpgradable">
<span class="fa fa-edit fa-fw mr-1" />
<span v-localize>Upgrade</span>
</a>
<a class="dropdown-item" @keypress="router.push(routeEdit)" @click.prevent="router.push(routeEdit)">
<span class="fa fa-edit fa-fw mr-1" />
<span v-localize>Edit configuration</span>
</a>
</div>
</div>
</template>
103 changes: 103 additions & 0 deletions client/src/components/ObjectStore/Instances/ManageIndex.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<script setup lang="ts">
import _l from "@/utils/localization";
import { computed } from "vue";
import { useRouter } from "vue-router/composables";
import InstanceDropdown from "./InstanceDropdown.vue";
import TemplateSummarySpan from "@/components/ObjectStore/Templates/TemplateSummarySpan.vue";
import ObjectStoreTypeSpan from "@/components/ObjectStore/ObjectStoreTypeSpan.vue";
import ObjectStoreBadges from "@/components/ObjectStore/ObjectStoreBadges.vue";
import { useObjectStoreInstancesStore } from "@/stores/objectStoreInstancesStore";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import LoadingSpan from "@/components/LoadingSpan.vue";
import "./icons";
const router = useRouter();
const objectStoreInstancesStore = useObjectStoreInstancesStore();
interface Props {
message: String | undefined | null;
}
defineProps<Props>();
const fields = [
{
key: "name",
label: _l("Name"),
sortable: true,
},
{
key: "description",
label: _l("Description"),
sortable: true,
},
{
key: "type",
label: _l("Type"),
sortable: true,
},
{
key: "template",
label: _l("From Template"),
sortable: true,
},
{
key: "badges",
label: _l(" "),
sortable: false,
},
];
const items = computed(() => objectStoreInstancesStore.getInstances);
const loading = computed(() => objectStoreInstancesStore.loading);
objectStoreInstancesStore.fetchInstances();
</script>

<template>
<div>
<p>
{{ message || "" }}
</p>
<b-row class="mb-3">
<b-col>
<b-button
id="workflow-create"
class="m-1 float-right"
@click="router.push('/object_store_instances/create')">
<FontAwesomeIcon icon="plus" />
{{ _l("Create") }}
</b-button>
</b-col>
</b-row>
<b-table
no-sort-reset
:fields="fields"
:items="items"
:hover="true"
:striped="true"
:caption-top="true"
:fixed="true"
:show-empty="true">
<template v-slot:empty>
<loading-span v-if="loading" message="Loading object store instances" />
<b-alert v-else id="no-object-store-instances" variant="info" show>
<div>No object store instances found, click the create button to configure a new one.</div>
</b-alert>
</template>
<template v-slot:cell(badges)="row">
<ObjectStoreBadges size="1x" :badges="row.item.badges" />
</template>
<template v-slot:cell(name)="row">
<InstanceDropdown :object-store="row.item" />
</template>
<template v-slot:cell(type)="row">
<ObjectStoreTypeSpan :type="row.item.type" />
</template>
<template v-slot:cell(template)="row">
<TemplateSummarySpan
:template-version="row.item.template_version ?? 0"
:template-id="row.item.template_id" />
</template>
</b-table>
</div>
</template>
21 changes: 21 additions & 0 deletions client/src/components/ObjectStore/Instances/UpgradeInstance.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
import { computed } from "vue";
import { useObjectStoreInstancesStore } from "@/stores/objectStoreInstancesStore";
import LoadingSpan from "@/components/LoadingSpan.vue";
const objectStoreInstancesStore = useObjectStoreInstancesStore();
objectStoreInstancesStore.fetchInstances();
interface Props {
instanceId: number;
}
const props = defineProps<Props>();
const instance = computed(() => objectStoreInstancesStore.getInstance(props.instanceId));
</script>
<template>
<div>
<loading-span v-if="!instance" message="Loading object store instance" />
<div v-else>Upgrade instance {{ instance?.variables }}.</div>
</div>
</template>
9 changes: 9 additions & 0 deletions client/src/components/ObjectStore/Instances/icons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* I can't get this to type properly in type script, so
handling it here in JavaScript. I get a variant of this
error (https://github.com/FortAwesome/Font-Awesome/issues/12575).
*/
import { library } from "@fortawesome/fontawesome-svg-core";
import { faCaretDown, faPlus } from "@fortawesome/free-solid-svg-icons";

library.add(faCaretDown);
library.add(faPlus);
14 changes: 14 additions & 0 deletions client/src/components/ObjectStore/Instances/services.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { fetcher } from "@/schema/fetcher";

export const create = fetcher.path("/api/object_store_instances").method("post").create();

// Previous create but it didn't do anything right?

/*
import type { CreateInstancePayload } from "./types";
export const createObjectStoreInstance = fetcher.path("/api/object_store_instances").method("post").create();
export async function create(payload: CreateInstancePayload) {
const { data } = await createObjectStoreInstance(payload);
return data;
}
*/
3 changes: 3 additions & 0 deletions client/src/components/ObjectStore/Instances/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { components } from "@/schema";
export type UserConcreteObjectStore = components["schemas"]["UserConcreteObjectStoreModel"];
export type CreateInstancePayload = components["schemas"]["CreateInstancePayload"];
2 changes: 0 additions & 2 deletions client/src/components/ObjectStore/ObjectStoreBadge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ const message = computed(() => {
<span ref="iconTarget" class="object-store-badge-wrapper">
<FontAwesomeLayers :class="layerClasses" :data-badge-type="badgeType">
<FontAwesomeIcon v-if="badgeType == 'restricted'" icon="user-lock" :class="disadvantage" />
<!--
<FontAwesomeIcon v-if="badgeType == 'user_defined'" icon="plug" :class="neutral" />
-->
<FontAwesomeIcon v-if="badgeType == 'quota'" icon="chart-line" :class="disadvantage" />
<FontAwesomeIcon v-if="badgeType == 'no_quota'" icon="chart-line" :class="neutral" v-bind="shrink" />
<FontAwesomeIcon v-if="badgeType == 'no_quota'" icon="ban" :class="[transparent, advantage]" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,9 @@ const title = computed(() => {
</script>

<template>
<span v-b-tooltip.hover class="stored-how" :title="title">{{ text }}</span>
<span v-b-tooltip.hover class="stored-how object-store-help-on-hover" :title="title">{{ text }}</span>
</template>

<style scoped>
/* Give visual indication of mouseover info */
.stored-how {
text-decoration-line: underline;
text-decoration-style: dashed;
}
@import "./style.css";
</style>
24 changes: 24 additions & 0 deletions client/src/components/ObjectStore/ObjectStoreTypeSpan.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script setup lang="ts">
import { computed } from "vue";
const MESSAGES = {
s3: "This is an object store based on the Amazon Simple Storage Service (S3) interface. This may or may not actually be configured to use Amazon infrastructure - the Amazon S3 interface has become an industry standard and many competing projects use a compatible interface.",
azure: "This is a Microsoft Azure Blob based object store. More information on Microsoft's Azure Blob Storage can be found at https://azure.microsoft.com/en-us/products/storage/blobs/.",
disk: "This is a simple path based object store that assumes the all the relevant paths are already mounted on the Galaxy server and target worker nodes.",
};
interface Props {
type: "s3" | "azure" | "disk";
}
const props = defineProps<Props>();
const title = computed<string>(() => MESSAGES[props.type] ?? "");
</script>

<template>
<span v-b-tooltip.hover class="object-store-type object-store-help-on-hover" :title="title">{{ type }}</span>
</template>

<style scoped>
@import "./style.css";
</style>
99 changes: 99 additions & 0 deletions client/src/components/ObjectStore/Templates/CreateForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<script lang="ts" setup>
import { computed } from "vue";
import type { ObjectStoreTemplateSummary, UserConcreteObjectStore } from "./types";
import FormCard from "@/components/Form/FormCard.vue";
import FormDisplay from "@/components/Form/FormDisplay.vue";
import { create } from "@/components/ObjectStore/Instances/services";
interface CreateFormProps {
template: ObjectStoreTemplateSummary;
}
const props = defineProps<CreateFormProps>();
const title = "Create a new object store for your data";
const submitTitle = "Submit";
const inputs = computed(() => {
const form = [];
const variables = props.template.variables ?? [];
const secrets = props.template.secrets ?? [];
form.push({
name: "_meta_name",
label: "Name",
type: "text",
optional: false,
help: "Label this new object store a name.",
});
form.push({
name: "_meta_description",
label: "Description",
optional: true,
type: "textarea",
help: "Provide some notes to yourself about this object store - perhaps to remind you how it is configured, where it stores the data, etc..",
});
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;
});
let formData: any = null;
async function onSubmit() {
const variables = props.template.variables ?? [];
const secrets = props.template.secrets ?? [];
const variableData: { [key: string]: (string | boolean | number) | undefined } = {};
const secretData: { [key: string]: string } = {};
for (const variable of variables) {
variableData[variable.name] = formData[variable.name];
}
for (const secret of secrets) {
secretData[secret.name] = formData[secret.name];
}
const name: string = formData._meta_name;
const description: string = formData._meta_description;
const payload = {
name: name,
description: description,
secrets: secretData,
variables: variableData,
template_id: props.template.id,
template_version: props.template.version ?? 0,
};
const { data: objectStore } = await create(payload);
emit("created", objectStore);
}
function onChange(incoming: any) {
formData = incoming;
}
const emit = defineEmits<{
(e: "created", objectStore: UserConcreteObjectStore): void;
}>();
</script>
<template>
<div>
<FormCard :title="title">
<template v-slot:body>
<FormDisplay :inputs="inputs" @onChange="onChange" />
</template>
</FormCard>
<div class="mt-3">
<b-button id="submit" variant="primary" class="mr-1" @click="onSubmit()">
{{ submitTitle }}
</b-button>
</div>
</div>
</template>
Loading

0 comments on commit 0cb7333

Please sign in to comment.