Skip to content

Commit

Permalink
fix(#117): task viewer refactor and fixes, driver converters refactor…
Browse files Browse the repository at this point in the history
…, new tab types support in serialization, properties table extensions and usage fixes
  • Loading branch information
lukashornych committed Oct 6, 2024
1 parent 934118c commit e2b34fb
Show file tree
Hide file tree
Showing 88 changed files with 2,084 additions and 1,071 deletions.
2 changes: 2 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { defaultToastOptions, toast } from '@/vue-plugins/toastification'
import vuetify from '@/vue-plugins/vuetify'
import Lab from '@/Lab.vue'
import { ModuleContextBuilder } from '@/ModuleContextBuilder'
import luxonExtensions from '@/vue-plugins/luxonExtensions'

/**
* Bootstraps the entire evitaLab.
Expand All @@ -27,6 +28,7 @@ loadFonts()
.use(VueApexCharts)
.use(i18n)
.use(router)
.use(luxonExtensions)

// register evitaLab modules
const moduleContextBuilder: ModuleContextBuilder = new ModuleContextBuilder(app)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { OffsetDateTime } from '@/modules/connection/model/data-type/OffsetDateTime'
import { computed, ref } from 'vue'
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import VFormDialog from '@/modules/base/component/VFormDialog.vue'
import { DateTime } from 'luxon'
Expand Down
165 changes: 69 additions & 96 deletions src/modules/backup-viewer/components/BackupViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,24 @@ import { useI18n } from 'vue-i18n'
import { VBtn, VIcon, VList } from 'vuetify/lib/components/index.mjs'
import { TabComponentProps } from '@/modules/workspace/tab/model/TabComponentProps'
import { VoidTabData } from '@/modules/workspace/tab/model/void/VoidTabData'
import { backupTaskName, restoreTaskName, BackupViewerService, useBackupViewerService } from '../service/BackupViewerService'
import { BackupViewerService, useBackupViewerService } from '../service/BackupViewerService'
import { onUnmounted } from 'vue'
import { BackupViewerTabParams } from '@/modules/backup-viewer/model/BackupViewerTabParams'
import { FilesToFetch } from '@/modules/connection/model/file/FilesToFetch'
import { TaskState } from '@/modules/connection/model/task/TaskState'
import BackupCatalogDialog from '@/modules/backup-viewer/components/BackupCatalogDialog.vue'
import VTabToolbar from '@/modules/base/component/VTabToolbar.vue'
import VTabMainActionButton from '@/modules/base/component/VTabMainActionButton.vue'
import { Command } from '@/modules/keymap/model/Command'
import VActionTooltip from '@/modules/base/component/VActionTooltip.vue'
import { Toaster, useToaster } from '@/modules/notification/service/Toaster'
import TasksVisualizer from '@/modules/task-viewer/components/TasksVisualizer.vue'
import { File } from '@/modules/connection/model/file/File'
import { ServerFile } from '@/modules/connection/model/server-file/ServerFile'
import RestoreCatalogDialog from '@/modules/backup-viewer/components/RestoreCatalogDialog.vue'
import { PaginatedList } from '@/modules/connection/model/PaginatedList'
import { backupTaskName } from '@/modules/backup-viewer/model/BackupTask'
import { restoreTaskName } from '@/modules/backup-viewer/model/RestoreTask'
import TaskList from '@/modules/task-viewer/components/TaskList.vue'
import VListItemDivider from '@/modules/base/component/VListItemDivider.vue'
const shownTaskStates: TaskState[] = [TaskState.Running, TaskState.Queued, TaskState.Failed]
const shownTaskTypes: string[] = [backupTaskName, restoreTaskName]
const backupViewerService: BackupViewerService = useBackupViewerService()
const toaster: Toaster = useToaster()
Expand All @@ -28,65 +32,82 @@ const { t } = useI18n()
const emit = defineEmits<TabComponentEvents>()
const props = defineProps<TabComponentProps<BackupViewerTabParams, VoidTabData>>()
const path: List<string> = List([t('backupViewer.path')])
const initialized = ref<boolean>(false)
const taskListRef = ref<typeof TaskList>()
const backupsIntervalId = setInterval(loadBackupFiles, 2000)
const path: List<string> = List([props.params.catalogName])
onUnmounted(() => {
clearInterval(backupsIntervalId)
})
const initialized = ref<boolean>(false)
const showBackupCatalogDialog = ref<boolean>(false)
const showRestoreCatalogDialog = ref<boolean>(false)
const restoreBackupFile = ref<File | undefined>()
const restoreBackupFile = ref<ServerFile | undefined>()
const backupsInPreparationPresent = ref<boolean>(false)
const backupFilesLoaded = ref<boolean>(false)
const backupFiles = ref<FilesToFetch>()
const backupFileItems = computed<File[]>(() => {
const backupFiles = ref<PaginatedList<ServerFile>>()
const backupFileItems = computed<ServerFile[]>(() => {
if (backupFiles.value == undefined) {
return []
}
return backupFiles.value.filesToFetch.toArray()
return backupFiles.value.data.toArray()
})
const pageNumber = ref<number>(1)
watch(pageNumber, async () => {
await loadBackupFiles()
})
const pageCount = computed<number>(() => {
if (backupFiles.value == undefined) {
return 1
}
return Math.ceil(backupFiles.value.totalNumberOfRecords / pageSize.value)
})
const pageSize = ref<number>(20)
const file = ref<File>()
loadBackupFiles().then(() => {
initialized.value = true
emit('ready')
})
watch(pageNumber, async () => {
await loadBackupFiles()
})
async function loadBackupFiles(): Promise<void> {
async function loadBackupFiles(): Promise<boolean> {
try {
backupFiles.value = await backupViewerService.getBackupFiles(
props.params.connection,
pageNumber.value,
pageSize.value
)
backupFilesLoaded.value = true
return true
} catch (e: any) {
toaster.error(t(
'backupViewer.notification.couldNotLoadBackupFiles',
{ reason: e.message }
))
return false
}
}
loadBackupFiles().then(() => {
initialized.value = true
emit('ready')
})
let canReloadBackupFiles: boolean = true
async function reloadBackupFiles(manual: boolean = false): Promise<void> {
if (!canReloadBackupFiles && !manual) {
return
}
function showRestoreDialog(file: File): void {
const loaded: boolean = await loadBackupFiles()
if (loaded) {
canReloadBackupFiles = true
setTimeout(reloadBackupFiles, 2000)
} else {
// we don't want to spam user server is down, user needs to refresh manually
canReloadBackupFiles = false
}
}
setTimeout(reloadBackupFiles, 2000)
function reloadBackups(): void {
reloadBackupFiles(true)
taskListRef.value?.reload(true)
}
function showRestoreDialog(file: ServerFile): void {
restoreBackupFile.value = file
showRestoreCatalogDialog.value = true
}
Expand All @@ -96,7 +117,8 @@ function hideRestoreDialog(): void {
restoreBackupFile.value = undefined
}
async function downloadBackup(file: File){
// todo lho download button instead
async function downloadBackup(file: ServerFile){
try {
const blob = await backupViewerService.downloadBackup(props.params.connection, file.fileId)
Expand All @@ -116,87 +138,32 @@ async function downloadBackup(file: File){
))
}
}
// todo lho prototype for local file restore
// async function upload(){
// await backupViewerService.uploadBackup(props.params.connection, sendCatalogChunks())
// }
//
// async function* sendCatalogChunks(): AsyncIterable<GrpcRestoreCatalogRequest> {
// if (!file.value) {
// return;
// }
//
// const CHUNK_SIZE = 64 * 1024; // 64 KB chunks
// const fileReader = new FileReader();
// let offset = 0;
// const totalSize = file.value.size;
//
// // Helper function to read a chunk
// function readChunk() {
// if (offset >= totalSize) {
// fileReader.abort();
// return;
// }
// const chunk = file.value!.slice(offset, offset + CHUNK_SIZE);
// fileReader.readAsArrayBuffer(chunk);
// }
//
// // Promise to handle the load of one chunk
// const onLoadPromise = () => {
// return new Promise<Uint8Array>((resolve, reject) => {
// fileReader.onload = (event: ProgressEvent<FileReader>) => {
// if (event.target?.result) {
// const arrayBuffer = event.target.result as ArrayBuffer;
// const fileChunk = new Uint8Array(arrayBuffer);
// offset += CHUNK_SIZE;
// resolve(fileChunk);
// }
// };
//
// fileReader.onerror = () => {
// console.error('Error reading file.');
// fileReader.abort();
// reject(new Error('Error reading file'));
// };
// });
// };
//
// try {
// while (offset < totalSize) {
// readChunk();
// const chunk = await onLoadPromise();
// yield new GrpcRestoreCatalogRequest({
// catalogName: 'random',
// backupFile: chunk
// }); // Yield the chunk here
// }
// } catch (error) {
// console.error('Error during file upload:', error);
// }
// }
</script>

<template>
<div v-if="initialized" class="backup-viewer">
<VTabToolbar prepend-icon="mdi-cloud-download-outline" :path="path">
<template #append>
<VTabMainActionButton @click="showBackupCatalogDialog = true">
<VIcon>mdi-cloud-download-outline</VIcon>

<VBtn icon @click="reloadBackups">
<VIcon>mdi-refresh</VIcon>
<VTooltip activator="parent">
{{ t('backupViewer.button.reloadBackups') }}
</VTooltip>
</VBtn>
<VTabMainActionButton prepend-icon="mdi-cloud-download-outline" @click="showBackupCatalogDialog = true">
{{ t('backupViewer.button.backupCatalog') }}
</VTabMainActionButton>
</template>
</VTabToolbar>

<div>
<!-- todo lho add restore tasks -->
<TasksVisualizer
<TaskList
ref="taskListRef"
v-show="backupsInPreparationPresent"
:connection="params.connection"
:subheader="t('backupViewer.tasks.title')"
:states="[TaskState.Running, TaskState.Queued]"
:task-types="[backupTaskName, restoreTaskName]"
:states="shownTaskStates"
:task-types="shownTaskTypes"
:page-size="5"
hideable-pagination
@update:active-jobs-present="backupsInPreparationPresent = $event"
Expand Down Expand Up @@ -249,6 +216,12 @@ async function downloadBackup(file: File){
</VCol>
</VRow>
</VListItem>

<!-- todo lho implement -->
<!-- <VListItemDivider-->
<!-- v-if="index < taskStatusesItems.length - 1"-->
<!-- inset-->
<!-- />-->
</template>

<template #footer>
Expand Down
4 changes: 2 additions & 2 deletions src/modules/backup-viewer/components/RestoreCatalogDialog.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { computed, ref } from 'vue'
import { File } from '@/modules/connection/model/file/File'
import { ServerFile } from '@/modules/connection/model/server-file/ServerFile'
import { Connection } from '@/modules/connection/model/Connection'
import { BackupViewerService, useBackupViewerService } from '@/modules/backup-viewer/service/BackupViewerService'
import { Toaster, useToaster } from '@/modules/notification/service/Toaster'
Expand All @@ -15,7 +15,7 @@ const { t } = useI18n()
const props = defineProps<{
modelValue: boolean,
connection: Connection,
file: File
file: ServerFile
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void
Expand Down
4 changes: 4 additions & 0 deletions src/modules/backup-viewer/model/BackupTask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Represents name of server task creating catalog backup
*/
export const backupTaskName: string = 'BackupTask'
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { DefineComponent, markRaw } from "vue";
import { BackupViewerTabParams } from '@/modules/backup-viewer/model/BackupViewerTabParams'
import BackupViewer from '@/modules/backup-viewer/components/BackupViewer.vue'

export class BackupViewerDefinition extends TabDefinition<BackupViewerTabParams, VoidTabData> {
export class BackupViewerTabDefinition extends TabDefinition<BackupViewerTabParams, VoidTabData> {
constructor(title: string, params: BackupViewerTabParams) {
super(undefined,
super(
undefined,
title,
'mdi-cloud-download-outline',
markRaw(BackupViewer as DefineComponent<any, any, any>),
Expand Down
10 changes: 4 additions & 6 deletions src/modules/backup-viewer/model/BackupViewerTabParams.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import { TabParams } from "@/modules/workspace/tab/model/TabParams";
import { BackupViewerTabParamsDto } from "./BackupViewerTabParamsDto";
import { ExecutableTabRequest } from "@/modules/workspace/tab/model/ExecutableTabRequest";
import { Connection } from "@/modules/connection/model/Connection";

export class BackupViewerTabParams implements TabParams<BackupViewerTabParamsDto>, ExecutableTabRequest {
readonly executeOnOpen: boolean
export class BackupViewerTabParams implements TabParams<BackupViewerTabParamsDto> {

readonly connection: Connection
readonly catalogName: string

constructor(connection: Connection, catalogName: string, executeOnOpen: boolean = false) {
constructor(connection: Connection, catalogName: string) {
this.connection = connection
this.executeOnOpen = executeOnOpen
this.catalogName = catalogName
}
toSerializable(): BackupViewerTabParamsDto {
return {
connection: this.connection,
connectionId: this.connection.id,
catalogName: this.catalogName
}
}
Expand Down
5 changes: 2 additions & 3 deletions src/modules/backup-viewer/model/BackupViewerTabParamsDto.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Connection } from "@/modules/connection/model/Connection";
import { TabDataDto } from "@/modules/workspace/tab/model/TabDataDto";
import { TabParamsDto } from '@/modules/workspace/tab/model/TabParamsDto'
import { ConnectionId } from '@/modules/connection/model/ConnectionId'

export interface BackupViewerTabParamsDto extends TabParamsDto {
readonly connection: Connection
readonly connectionId: ConnectionId
readonly catalogName: string
}
4 changes: 4 additions & 0 deletions src/modules/backup-viewer/model/RestoreTask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Represents name of server task restoring catalog from file
*/
export const restoreTaskName: string = 'RestoreTask'
Loading

0 comments on commit e2b34fb

Please sign in to comment.