From 759aa81488534360cadc8480c4aba7dac18ffee3 Mon Sep 17 00:00:00 2001 From: dvince Date: Tue, 11 Feb 2025 15:21:59 -0500 Subject: [PATCH] [BUG]: download model-configurations is broken #6407 Adding a new option to download a configured model in addition to the zipped archive --- .../tera-model-config-drilldown.vue | 23 ++++++++++++++++--- .../tera-model-configuration-item.vue | 13 ++++++++--- .../src/services/model-configurations.ts | 2 +- .../client/hmi-client/src/services/model.ts | 2 +- .../ModelConfigurationController.java | 6 ++--- 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/packages/client/hmi-client/src/components/workflow/ops/model-config/tera-model-config-drilldown.vue b/packages/client/hmi-client/src/components/workflow/ops/model-config/tera-model-config-drilldown.vue index 587ff74a32..536ec9a692 100644 --- a/packages/client/hmi-client/src/components/workflow/ops/model-config/tera-model-config-drilldown.vue +++ b/packages/client/hmi-client/src/components/workflow/ops/model-config/tera-model-config-drilldown.vue @@ -55,7 +55,8 @@ :empty-input-count="missingInputCount(configuration)" @click="onSelectConfiguration(configuration)" @delete="fetchConfigurations(model.id)" - @download="downloadModelArchive(configuration)" + @downloadArchive="downloadZippedModelAndConfig(configuration)" + @downloadModel="downloadModel(configuration)" @use="onSelectConfiguration(configuration)" /> @@ -263,7 +264,8 @@ import { setInitialSource, setParameterDistributions, setParameterSource, - updateModelConfiguration + updateModelConfiguration, + getAsConfiguredModel } from '@/services/model-configurations'; import { useToastService } from '@/services/toast'; import type { Initial, Model, ModelConfiguration, TaskResponse } from '@/types/Types'; @@ -566,7 +568,7 @@ const configuredMmt = ref(makeConfiguredMMT(mmt.value, knobs.value.transientMode const calendarSettings = ref(null); -const downloadModelArchive = async (configuration: ModelConfiguration = knobs.value.transientModelConfig) => { +const downloadZippedModelAndConfig = async (configuration: ModelConfiguration = knobs.value.transientModelConfig) => { const archive = await getArchive(configuration); if (archive) { const a = document.createElement('a'); @@ -577,6 +579,21 @@ const downloadModelArchive = async (configuration: ModelConfiguration = knobs.va } }; +const downloadModel = async (configuration: ModelConfiguration = knobs.value.transientModelConfig) => { + if (!configuration.id) return; + + const configuredModel = await getAsConfiguredModel(configuration.id); + if (configuredModel) { + const data = `text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(configuredModel, null, 2))}`; + const a = document.createElement('a'); + a.href = `data:${data}`; + a.download = `${configuredModel.name ?? 'model'}.json`; + a.innerHTML = 'download JSON'; + a.click(); + a.remove(); + } +}; + const createConfiguration = async () => { if (!model.value || isSaveDisabled.value) return; const modelConfig = cloneDeep(knobs.value.transientModelConfig); diff --git a/packages/client/hmi-client/src/components/workflow/ops/model-config/tera-model-configuration-item.vue b/packages/client/hmi-client/src/components/workflow/ops/model-config/tera-model-configuration-item.vue index f25f8d23d3..06f5aa6ccd 100644 --- a/packages/client/hmi-client/src/components/workflow/ops/model-config/tera-model-configuration-item.vue +++ b/packages/client/hmi-client/src/components/workflow/ops/model-config/tera-model-configuration-item.vue @@ -27,7 +27,7 @@ import { ref } from 'vue'; import { useConfirm } from 'primevue/useconfirm'; import { deleteModelConfiguration } from '@/services/model-configurations'; -const emit = defineEmits(['delete', 'use', 'download']); +const emit = defineEmits(['delete', 'use', 'downloadArchive', 'downloadModel']); const props = defineProps<{ configuration: ModelConfiguration; selected?: boolean; @@ -46,10 +46,17 @@ const contextMenuItems = ref([ } }, { - label: 'Download', + label: 'Download model configuration', icon: 'pi pi-download', command() { - emit('download'); + emit('downloadArchive'); + } + }, + { + label: 'Download as Model', + icon: 'pi pi-download', + command() { + emit('downloadModel'); } }, { diff --git a/packages/client/hmi-client/src/services/model-configurations.ts b/packages/client/hmi-client/src/services/model-configurations.ts index 4bd63225b3..35360e2aef 100644 --- a/packages/client/hmi-client/src/services/model-configurations.ts +++ b/packages/client/hmi-client/src/services/model-configurations.ts @@ -53,7 +53,7 @@ export const getAsConfiguredModel = async (modelConfigurationId: string): Promis }; export const getArchive = async (modelConfiguration: ModelConfiguration): Promise => { - const response = await API.get(`model-configurations/download/${modelConfiguration.id}`, { + const response = await API.get(`model-configurations/download-archive/${modelConfiguration.id}`, { responseType: 'arraybuffer' }); const blob = new Blob([response?.data], { type: 'application/octet-stream' }); diff --git a/packages/client/hmi-client/src/services/model.ts b/packages/client/hmi-client/src/services/model.ts index dfd77becf2..905b0dc1da 100644 --- a/packages/client/hmi-client/src/services/model.ts +++ b/packages/client/hmi-client/src/services/model.ts @@ -29,7 +29,7 @@ export async function createModelAndModelConfig(file: File, progress?: Ref getModelConfiguration( } } - @PostMapping(value = "/import", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping(value = "/import-archive", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @Secured(Roles.USER) @Operation(summary = "Imports both a model and its configuration in a single go") @ApiResponses( @@ -180,9 +180,9 @@ public ResponseEntity importModelConfigAndModel( } } - @GetMapping("/download/{id}") + @GetMapping("/download-archive/{id}") @Secured(Roles.USER) - @Operation(summary = "Imports both a model and its configuration in a single go") + @Operation(summary = "Downloads both a model and its configuration in a single go in a zipped archive") @ApiResponses( value = { @ApiResponse(