diff --git a/AdminWebApp/src/api/client.ts b/AdminWebApp/src/api/client.ts index b78269ffb..6dfed6bd5 100644 --- a/AdminWebApp/src/api/client.ts +++ b/AdminWebApp/src/api/client.ts @@ -659,8 +659,8 @@ export class Client { return this.postRawReturn[]>(`/containers/${containerID}/import/datasources/${dataSourceID}/mappings/upgrade`, payload); } - dataSourceJSONFileImport(containerID: string, dataSourceID: string, file: File): Promise { - return this.postFile(`/containers/${containerID}/import/datasources/${dataSourceID}/imports`, 'import', file); + dataSourceJSONFileImport(containerID: string, dataSourceID: string, file: File, fastload?: boolean): Promise { + return this.postFile(`/containers/${containerID}/import/datasources/${dataSourceID}/imports`, 'import', file, {fastLoad: fastload}); } async uploadFile(containerID: string, dataSourceID: string, file: File): Promise { @@ -1565,7 +1565,7 @@ export class Client { }); } - private async postFile(uri: string, inputName: string, file: File): Promise { + private async postFile(uri: string, inputName: string, file: File, queryParams?: {[key: string]: any}): Promise { const config: AxiosRequestConfig = {}; config.headers = {'Access-Control-Allow-Origin': '*', 'Content-Type': 'multipart/form-data'}; config.validateStatus = () => { @@ -1580,10 +1580,17 @@ export class Client { config.auth = {username: this.config.username, password: this.config.password} as AxiosBasicCredentials; } + let url: string; + if (queryParams) { + url = buildURL(this.config?.rootURL!, {path: uri, queryParams: queryParams!}); + } else { + url = buildURL(this.config?.rootURL!, {path: uri}) + } + const formData = new FormData(); formData.append(inputName, file); - const resp: AxiosResponse = await axios.post(buildURL(this.config?.rootURL!, {path: uri}), formData, config); + const resp: AxiosResponse = await axios.post(url, formData, config); return new Promise((resolve, reject) => { if (resp.status < 200 || resp.status > 299) reject(resp.data.error); diff --git a/AdminWebApp/src/api/types.ts b/AdminWebApp/src/api/types.ts index 363848f21..32e65b774 100644 --- a/AdminWebApp/src/api/types.ts +++ b/AdminWebApp/src/api/types.ts @@ -260,6 +260,7 @@ export type TimeseriesDataSourceConfig = { value_nodes?: string[]; data_retention_days?: number; raw_retention_enabled?: boolean; + fast_load_enabled?: boolean; }; export type HttpDataSourceConfig = { @@ -804,5 +805,6 @@ export function DefaultTimeseriesDataSourceConfig(): TimeseriesDataSourceConfig kind: 'timeseries', columns: [], attachment_parameters: [], + fast_load_enabled: true, }; } diff --git a/AdminWebApp/src/components/dataImport/importDataDialog.vue b/AdminWebApp/src/components/dataImport/importDataDialog.vue index ec9253b1c..eec8a2573 100644 --- a/AdminWebApp/src/components/dataImport/importDataDialog.vue +++ b/AdminWebApp/src/components/dataImport/importDataDialog.vue @@ -12,14 +12,11 @@ - - - - - + + @@ -50,6 +47,9 @@ @Prop({required: false, default: false}) disabled!: boolean + @Prop({required: false, default: false}) + fastload!: boolean + errorMessage = "" loading = false dialog = false @@ -67,7 +67,7 @@ uploadImport() { this.loading = true if(this.filesToUpload) { - this.$client.dataSourceJSONFileImport(this.containerID, this.dataSourceID, this.filesToUpload) + this.$client.dataSourceJSONFileImport(this.containerID, this.dataSourceID, this.filesToUpload, this.fastload) .then(() => { this.dialog = false this.errorMessage = "" diff --git a/AdminWebApp/src/components/dataSources/createDataSourceDialog.vue b/AdminWebApp/src/components/dataSources/createDataSourceDialog.vue index 2eaecbb34..325fc7678 100644 --- a/AdminWebApp/src/components/dataSources/createDataSourceDialog.vue +++ b/AdminWebApp/src/components/dataSources/createDataSourceDialog.vue @@ -390,6 +390,11 @@
+ +

{{$t('createDataSource.description')}}

{{$t('createDataSource.timeseriesDescription')}} {{$t('dataMapping.here')}}.

@@ -465,19 +470,23 @@ - + - + :label="$t('dataMapping.isPrimaryTimestamp')" + v-model="item.is_primary_timestamp" + disabled + /> @@ -632,6 +641,17 @@ export default class CreateDataSourceDialog extends Vue { } hideP6pass = true authorized: string[] = [] + fastload = true + defaultFormatString = '%Y-%m-%d %H:%M:%S.%f' + rules={ + dateString: (value: any) => { + if (this.getFastload()) { + return value.includes('%') || 'Date String should be in strftime datetime format' + } else { + return !value.includes('%') || 'Date String should be in postgres datetime format' + } + } + } newDataSource: DataSourceT = { name: "", @@ -668,6 +688,37 @@ export default class CreateDataSourceDialog extends Vue { } } + // for some reason calling the variable itself in the validation always results + // in the default "true". this getter is a workaround for that + getFastload() { + return this.fastload + } + + @Watch('fastload') + dateStringChange() { + if (this.fastload) { + this.defaultFormatString = '%Y-%m-%d %H:%M:%S.%f' + } else { + this.defaultFormatString = 'YYYY-MM-DD HH24:MI:SS.US' + } + this.checkDateStrings() + } + + checkDateStrings() { + this.timeseriesConfig.columns.forEach(col => { + if (col.date_conversion_format_string && col.date_conversion_format_string !== this.defaultFormatString) { + // if fastload enabled and no percent symbols, assume that string needs to be reformatted to rust format + // similarly, if fastload disabled but percent symbols present, reformat string to pg format + if ( + (this.fastload && !col.date_conversion_format_string.includes('%')) + || (!this.fastload && col.date_conversion_format_string.includes('%')) + ) { + col.date_conversion_format_string = this.defaultFormatString + } + } + }) + } + beforeMount() { this.container = this.$store.getters.activeContainer; } @@ -712,6 +763,13 @@ export default class CreateDataSourceDialog extends Vue { ] } + timeseriesHelpLink(): string { + if (this.fastload === true) { + return "https://docs.rs/chrono/0.4.24/chrono/format/strftime/index.html" + } + return "https://www.postgresql.org/docs/current/functions-formatting.html#FUNCTIONS-FORMATTING-DATETIME-TABLE" + } + selectAdapter(adapter: string) { this.newDataSource.adapter_type = adapter } @@ -786,6 +844,7 @@ export default class CreateDataSourceDialog extends Vue { } case "timeseries": { + this.timeseriesConfig.fast_load_enabled = this.fastload; this.newDataSource.config = this.timeseriesConfig; this.newDataSource.active = true; break; @@ -895,7 +954,7 @@ export default class CreateDataSourceDialog extends Vue { is_primary_timestamp: true, unique: false, type: 'date', - date_conversion_format_string: 'YYYY-MM-DD HH24:MI:SS.US', + date_conversion_format_string: this.fastload ? '%Y-%m-%d %H:%M:%S.%f' : 'YYYY-MM-DD HH24:MI:SS.US', }) this.expandedTimeSeries.push(this.timeseriesConfig.columns[0]) diff --git a/AdminWebApp/src/components/dataSources/editDataSourceDialog.vue b/AdminWebApp/src/components/dataSources/editDataSourceDialog.vue index f94e82bb1..3d3fa41dd 100644 --- a/AdminWebApp/src/components/dataSources/editDataSourceDialog.vue +++ b/AdminWebApp/src/components/dataSources/editDataSourceDialog.vue @@ -362,6 +362,11 @@
+ +

{{$t('dataMapping.tableDesign')}}

- + - + :label="$t('dataMapping.isPrimaryTimestamp')" + v-model="item.is_primary_timestamp" + disabled + /> @@ -542,7 +551,7 @@