This is an automated email from the ASF dual-hosted git repository.
zehnder pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git
The following commit(s) were added to refs/heads/dev by this push:
new 4495b463b Update of PR #2300 (#2356)
4495b463b is described below
commit 4495b463bc000f92e562217a2dd164b754cb82ba
Author: Philipp Zehnder <[email protected]>
AuthorDate: Fri Dec 29 08:46:17 2023 +0100
Update of PR #2300 (#2356)
* Serialize non-primitive types and store in Influx
* extract RawFieldSerializer
* rename test
* delete old
* temp
* redesign file api
* fix
* Update
streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v095/DuplicateFilesRenameMigration.java
Co-authored-by: Tim <[email protected]>
* Update
ui/src/app/files/dialog/file-rename/file-rename-dialog.component.html
Co-authored-by: Tim <[email protected]>
* Update
streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineElementFile.java
Co-authored-by: Tim <[email protected]>
* Update
streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineElementFile.java
Co-authored-by: Tim <[email protected]>
* refactor(#2181): Refactor DuplicateFilesRenameMigration
* Add comment for migration
* fix(#2356): Fix linting error
* fix(#2356): Fix checkstyle proble
---------
Co-authored-by: muyangye <[email protected]>
Co-authored-by: Tim <[email protected]>
---
.../streampipes/rest/impl/PipelineElementFile.java | 19 ++-
streampipes-service-core/pom.xml | 6 +
.../core/migrations/AvailableMigrations.java | 4 +-
.../v095/DuplicateFilesRenameMigration.java | 142 +++++++++++++++++++++
.../v095/DuplicateFilesRenameMigrationTest.java | 120 +++++++++++++++++
.../storage/api/IFileMetadataStorage.java | 2 +
.../couchdb/impl/FileMetadataStorageImpl.java | 5 +
.../src/lib/apis/files.service.ts | 6 +
.../static-file-input.component.ts | 86 ++++++++++---
.../file-rename/file-rename-dialog.component.html | 72 +++++++++++
.../file-rename/file-rename-dialog.component.scss | 18 +--
.../file-rename/file-rename-dialog.component.ts | 34 ++---
.../file-upload/file-upload-dialog.component.html | 42 +++++-
.../file-upload/file-upload-dialog.component.ts | 39 +++++-
ui/src/app/files/files.module.ts | 4 +
15 files changed, 539 insertions(+), 60 deletions(-)
diff --git
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineElementFile.java
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineElementFile.java
index 72a3f4d0a..2a71a5a0a 100644
---
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineElementFile.java
+++
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineElementFile.java
@@ -61,7 +61,8 @@ public class PipelineElementFile extends
AbstractAuthGuardedRestResource {
FileManager.storeFile(
getAuthenticatedUsername(),
fileDetail.getOriginalFilename(),
- fileDetail.getInputStream());
+ fileDetail.getInputStream()
+ );
return ok(metadata);
} catch (Exception e) {
return fail();
@@ -78,7 +79,8 @@ public class PipelineElementFile extends
AbstractAuthGuardedRestResource {
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize(AuthConstants.HAS_READ_FILE_PRIVILEGE)
public ResponseEntity<List<FileMetadata>> getFileInfo(
- @RequestParam(value = "filetypes", required = false) String filetypes) {
+ @RequestParam(value = "filetypes", required = false) String filetypes
+ ) {
return ok(FileManager.getAllFiles(filetypes));
}
@@ -119,11 +121,22 @@ public class PipelineElementFile extends
AbstractAuthGuardedRestResource {
} catch (IOException e) {
throw new SpMessageException(
org.springframework.http.HttpStatus.NOT_FOUND,
- Notifications.error("File not found"));
+ Notifications.error("File not found")
+ );
}
}
private byte[] getFileContents(File file) throws IOException {
return Files.readAllBytes(file.toPath());
}
+
+ @GetMapping(path = "/allFilenames", produces =
MediaType.APPLICATION_JSON_VALUE)
+ @PreAuthorize(AuthConstants.HAS_READ_FILE_PRIVILEGE)
+ public ResponseEntity<List<String>> getAllOriginalFilenames() {
+ return ok(FileManager.getAllFiles()
+ .stream()
+ .map(fileMetadata ->
fileMetadata.getOriginalFilename()
+ .toLowerCase())
+ .toList());
+ }
}
diff --git a/streampipes-service-core/pom.xml b/streampipes-service-core/pom.xml
index fdb5c5fd1..48390b1c7 100644
--- a/streampipes-service-core/pom.xml
+++ b/streampipes-service-core/pom.xml
@@ -108,6 +108,12 @@
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
</dependency>
+ <!-- Test dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
index b389241b4..be23f8f0d 100644
---
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
+++
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
@@ -25,6 +25,7 @@ import
org.apache.streampipes.service.core.migrations.v070.CreateFileAssetTypeMi
import
org.apache.streampipes.service.core.migrations.v090.UpdateUsernameViewMigration;
import org.apache.streampipes.service.core.migrations.v093.AdapterMigration;
import
org.apache.streampipes.service.core.migrations.v093.StoreEmailTemplatesMigration;
+import
org.apache.streampipes.service.core.migrations.v095.DuplicateFilesRenameMigration;
import java.util.Arrays;
import java.util.List;
@@ -38,7 +39,8 @@ public class AvailableMigrations {
new CreateFileAssetTypeMigration(),
new UpdateUsernameViewMigration(),
new AdapterMigration(),
- new StoreEmailTemplatesMigration()
+ new StoreEmailTemplatesMigration(),
+ new DuplicateFilesRenameMigration()
);
}
}
diff --git
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v095/DuplicateFilesRenameMigration.java
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v095/DuplicateFilesRenameMigration.java
new file mode 100644
index 000000000..e5e1f7647
--- /dev/null
+++
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v095/DuplicateFilesRenameMigration.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.service.core.migrations.v095;
+
+import org.apache.streampipes.model.file.FileMetadata;
+import org.apache.streampipes.service.core.migrations.Migration;
+import org.apache.streampipes.storage.management.StorageDispatcher;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+public class DuplicateFilesRenameMigration implements Migration {
+ @Override
+ public boolean shouldExecute() {
+ return true;
+ }
+
+ // Starting from v0.95, StreamPipes will use file name as the unique
identifier of files
+ // This migration renames all the files that have duplicate names to ensure
uniqueness
+ @Override
+ public void executeMigration() {
+ var fileMetadataStorage = StorageDispatcher.INSTANCE.getNoSqlStore()
+
.getFileMetadataStorage();
+
+ var filesToUpdate =
getFilesToUpdate(fileMetadataStorage.getAllFileMetadataDescriptions());
+
+ filesToUpdate.forEach(fileMetadata ->
fileMetadataStorage.updateFileMetadata(fileMetadata));
+ }
+
+
+ /**
+ * Takes the list of files and groups all files with the same name together.
+ * The result is a list for each file name that has more than one file
associated with it.
+ */
+ private List<List<FileMetadata>>
getListsOfFilesWithSameName(List<FileMetadata> filesWithOldName) {
+ var duplicateFileMap = filesWithOldName.stream()
+ .collect(
+ Collectors.groupingBy(file ->
file.getOriginalFilename()
+
.toLowerCase()));
+ return duplicateFileMap.values()
+ .stream()
+ .filter(files -> files.size() > 1)
+ .toList();
+ }
+
+ @Override
+ public String getDescription() {
+ return "Rename files with duplicate names.";
+ }
+
+ /**
+ * Takes the files searches for duplicates and renames them and returns the
files that must be updated
+ */
+ protected List<FileMetadata> getFilesToUpdate(List<FileMetadata>
filesWithOldName) {
+
+ var groupsOfFilesWithSameName =
getListsOfFilesWithSameName(filesWithOldName);
+
+ return groupsOfFilesWithSameName.stream()
+ .flatMap(filesWithSameName ->
+
renameFilesWithSameName(filesWithSameName).stream())
+ .collect(Collectors.toList());
+ }
+
+
+ /**
+ * Takes a list of files with the same name renames them and returns the
updated list
+ */
+ private List<FileMetadata> renameFilesWithSameName(List<FileMetadata>
filesWithSameName) {
+ return IntStream.range(1, filesWithSameName.size())
+ .mapToObj(i -> {
+ var metadata = filesWithSameName.get(i);
+ return renameFile(metadata, i);
+ })
+ .toList();
+ }
+
+
+ /**
+ * Takes a file and renames it with a new name based the number of
occurrences of the file name
+ */
+ private FileMetadata renameFile(FileMetadata fileMetadata, int index) {
+ var oldFilename = fileMetadata.getOriginalFilename();
+
+ var fileNameWithoutType = removeFileType(oldFilename);
+ var fileTypeSuffix = getFileType(oldFilename);
+
+ var newFileName = createNewFileName(index, fileNameWithoutType,
fileTypeSuffix);
+
+ fileMetadata.setOriginalFilename(newFileName);
+
+ return fileMetadata;
+ }
+
+ /**
+ * Creates the new file name for a file with a duplicate name.
+ */
+ private String createNewFileName(
+ int index,
+ String fileName,
+ String fileType
+ ) {
+ return String.format(
+ "%s(%d)%s",
+ fileName,
+ index + 1,
+ fileType
+ );
+ }
+
+ /**
+ * Returns file name without file type suffix.
+ */
+ private String removeFileType(String fileName) {
+ var indexBeforeFileType = fileName.lastIndexOf('.');
+ return fileName.substring(0, indexBeforeFileType);
+ }
+
+ /**
+ * Returns the file type a given file name.
+ */
+ private String getFileType(String fileName) {
+ var indexBeforeFileType = fileName.lastIndexOf('.');
+ return fileName.substring(indexBeforeFileType);
+ }
+}
diff --git
a/streampipes-service-core/src/test/java/org/apache/streampipes/service/core/migrations/v095/DuplicateFilesRenameMigrationTest.java
b/streampipes-service-core/src/test/java/org/apache/streampipes/service/core/migrations/v095/DuplicateFilesRenameMigrationTest.java
new file mode 100644
index 000000000..18b35846f
--- /dev/null
+++
b/streampipes-service-core/src/test/java/org/apache/streampipes/service/core/migrations/v095/DuplicateFilesRenameMigrationTest.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.service.core.migrations.v095;
+
+
+import org.apache.streampipes.model.file.FileMetadata;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class DuplicateFilesRenameMigrationTest {
+
+ private DuplicateFilesRenameMigration migration;
+
+ private static final String FILE_NAME = "file.txt";
+ private static final String NEW_FILE_NAME_2 = "file(2).txt";
+ private static final String NEW_FILE_NAME_3 = "file(3).txt";
+
+ @Before
+ public void setUp() {
+ migration = new DuplicateFilesRenameMigration();
+ }
+
+ @Test
+ public void getFilesToUpdateHandlesNoDuplicates() {
+ var filesWithOldName = createFiles(List.of(
+ FILE_NAME
+ ));
+ var result = migration.getFilesToUpdate(filesWithOldName);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ public void getFilesToUpdateHandlesSingleDuplicate() {
+ var filesWithOldName = createFiles(List.of(
+ FILE_NAME,
+ FILE_NAME
+ ));
+ var result = migration.getFilesToUpdate(filesWithOldName);
+ assertEquals(1, result.size());
+ assertEquals(
+ NEW_FILE_NAME_2,
+ result.get(0)
+ .getOriginalFilename()
+ );
+ }
+
+ @Test
+ public void getFilesToUpdateHandlesMultipleDuplicates() {
+ var filesWithOldName = createFiles(List.of(
+ FILE_NAME,
+ FILE_NAME,
+ FILE_NAME
+ ));
+ var result = migration.getFilesToUpdate(filesWithOldName);
+ assertEquals(2, result.size());
+ assertEquals(
+ NEW_FILE_NAME_2,
+ result.get(0)
+ .getOriginalFilename()
+ );
+ assertEquals(
+ NEW_FILE_NAME_3,
+ result.get(1)
+ .getOriginalFilename()
+ );
+ }
+
+ @Test
+ public void getFilesToUpdateHandlesMixedDuplicatesAndUniqueFiles() {
+ var filesWithOldName = createFiles(List.of(
+ FILE_NAME,
+ FILE_NAME,
+ "unique.txt"
+ ));
+ var result = migration.getFilesToUpdate(filesWithOldName);
+ assertEquals(1, result.size());
+ assertEquals(
+ NEW_FILE_NAME_2,
+ result.get(0)
+ .getOriginalFilename()
+ );
+ }
+
+ /**
+ * Creates a list of FileMetadata objects from a list of file names.
+ */
+ private List<FileMetadata> createFiles(List<String> fileNames) {
+ return fileNames.stream()
+ .map(this::createFileMetadata)
+ .toList();
+ }
+
+ private FileMetadata createFileMetadata(String fileName) {
+ var fileMetadata = new FileMetadata();
+ fileMetadata.setOriginalFilename(fileName);
+ return fileMetadata;
+ }
+}
\ No newline at end of file
diff --git
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IFileMetadataStorage.java
b/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IFileMetadataStorage.java
index 4abaa66c7..13eae66ed 100644
---
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IFileMetadataStorage.java
+++
b/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IFileMetadataStorage.java
@@ -32,4 +32,6 @@ public interface IFileMetadataStorage {
void deleteFileMetadata(String id);
void addFileMetadata(FileMetadata fileMetadata);
+
+ void updateFileMetadata(FileMetadata fileMetadata);
}
diff --git
a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/FileMetadataStorageImpl.java
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/FileMetadataStorageImpl.java
index e55819946..618b10885 100644
---
a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/FileMetadataStorageImpl.java
+++
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/FileMetadataStorageImpl.java
@@ -65,4 +65,9 @@ public class FileMetadataStorageImpl extends
AbstractDao<FileMetadata> implement
public void addFileMetadata(FileMetadata fileMetadata) {
persist(fileMetadata);
}
+
+ @Override
+ public void updateFileMetadata(FileMetadata fileMetadata) {
+ update(fileMetadata);
+ }
}
diff --git
a/ui/projects/streampipes/platform-services/src/lib/apis/files.service.ts
b/ui/projects/streampipes/platform-services/src/lib/apis/files.service.ts
index 1439093ad..2c7f759bb 100644
--- a/ui/projects/streampipes/platform-services/src/lib/apis/files.service.ts
+++ b/ui/projects/streampipes/platform-services/src/lib/apis/files.service.ts
@@ -90,4 +90,10 @@ export class FilesService {
},
);
}
+
+ getAllOriginalFilenames(): Observable<string[]> {
+ return this.http.get<string[]>(
+ this.platformServicesCommons.apiBasePath + '/files/allFilenames',
+ );
+ }
}
diff --git
a/ui/src/app/core-ui/static-properties/static-file-input/static-file-input.component.ts
b/ui/src/app/core-ui/static-properties/static-file-input/static-file-input.component.ts
index 9ad435b11..079a7449f 100644
---
a/ui/src/app/core-ui/static-properties/static-file-input/static-file-input.component.ts
+++
b/ui/src/app/core-ui/static-properties/static-file-input/static-file-input.component.ts
@@ -16,7 +16,13 @@
*
*/
-import { Component, EventEmitter, OnInit, Output } from '@angular/core';
+import {
+ Component,
+ EventEmitter,
+ NgModule,
+ OnInit,
+ Output,
+} from '@angular/core';
import { HttpEventType, HttpResponse } from '@angular/common/http';
import {
FilesService,
@@ -26,6 +32,8 @@ import {
import { ConfigurationInfo } from '../../../connect/model/ConfigurationInfo';
import { AbstractValidatedStaticPropertyRenderer } from
'../base/abstract-validated-static-property';
import { UntypedFormControl, ValidatorFn, Validators } from '@angular/forms';
+import { MatDialog, MatDialogRef } from '@angular/material/dialog';
+import { FileRenameDialogComponent } from
'../../../files/dialog/file-rename/file-rename-dialog.component';
@Component({
selector: 'sp-static-file-input',
@@ -40,6 +48,8 @@ export class StaticFileInputComponent
public chooseExistingFileControl = new UntypedFormControl();
+ dialogRef: MatDialogRef<FileRenameDialogComponent>;
+
fileName: string;
selectedUploadFile: File;
@@ -54,7 +64,10 @@ export class StaticFileInputComponent
filesLoaded = false;
- constructor(private filesService: FilesService) {
+ constructor(
+ private filesService: FilesService,
+ public dialog: MatDialog,
+ ) {
super();
}
@@ -122,24 +135,44 @@ export class StaticFileInputComponent
}
upload() {
- this.uploadStatus = 0;
if (this.selectedUploadFile !== undefined) {
- this.filesService.uploadFile(this.selectedUploadFile).subscribe(
- event => {
- if (event.type === HttpEventType.UploadProgress) {
- this.uploadStatus = Math.round(
- (100 * event.loaded) / event.total,
- );
- } else if (event instanceof HttpResponse) {
- const internalFilename = event.body.internalFilename;
- this.parentForm.controls[this.fieldName].setValue(
- internalFilename,
- );
- this.fetchFileMetadata(internalFilename);
+ this.filesService
+ .getAllOriginalFilenames()
+ .subscribe(allFileNames => {
+ if (
+ !allFileNames.includes(
+ this.selectedUploadFile.name.toLowerCase(),
+ )
+ ) {
+ this.uploadStatus = 0;
+ this.filesService
+ .uploadFile(this.selectedUploadFile)
+ .subscribe(
+ event => {
+ if (
+ event.type ===
+ HttpEventType.UploadProgress
+ ) {
+ this.uploadStatus = Math.round(
+ (100 * event.loaded) / event.total,
+ );
+ } else if (event instanceof HttpResponse) {
+ const internalFilename =
+ event.body.internalFilename;
+ this.parentForm.controls[
+ this.fieldName
+ ].setValue(internalFilename);
+ this.fetchFileMetadata(
+ internalFilename,
+ );
+ }
+ },
+ error => {},
+ );
+ } else {
+ this.openRenameDialog();
}
- },
- error => {},
- );
+ });
}
}
@@ -163,4 +196,21 @@ export class StaticFileInputComponent
this.staticProperty.locationPath = value.internalFilename;
this.parentForm.updateValueAndValidity();
}
+
+ openRenameDialog() {
+ this.dialogRef = this.dialog.open(FileRenameDialogComponent);
+ this.dialogRef.afterClosed().subscribe(data => {
+ if (data) {
+ this.fileName = data;
+ this.selectedUploadFile = new File(
+ [this.selectedUploadFile],
+ this.fileName,
+ {
+ type: this.selectedUploadFile.type,
+ lastModified: this.selectedUploadFile.lastModified,
+ },
+ );
+ }
+ });
+ }
}
diff --git
a/ui/src/app/files/dialog/file-rename/file-rename-dialog.component.html
b/ui/src/app/files/dialog/file-rename/file-rename-dialog.component.html
new file mode 100644
index 000000000..121faeac0
--- /dev/null
+++ b/ui/src/app/files/dialog/file-rename/file-rename-dialog.component.html
@@ -0,0 +1,72 @@
+<!--
+~ Licensed to the Apache Software Foundation (ASF) under one or more
+~ contributor license agreements. See the NOTICE file distributed with
+~ this work for additional information regarding copyright ownership.
+~ The ASF licenses this file to You under the Apache License, Version 2.0
+~ (the "License"); you may not use this file except in compliance with
+~ the License. You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+~
+-->
+
+<div class="sp-dialog-container">
+ <div class="sp-dialog-content p-15">
+ <div fxFlex="100">
+ <div fxFlex="100" style="margin: 5px; width: 100%">
+ <div fxLayout="column" style="width: 100%">
+ <div fxFlex="100" fxLayoutAlign="center" fxLayout="column">
+ <b>
+ <h4>
+ A file with the same name already exists.
Please
+ give your file a new name or select one of the
+ existing files. it from existing files or
rename
+ it:
+ </h4>
+ </b>
+ <div class="input-group">
+ <span style="margin-right: 10px"
+ >new filename:</span
+ >
+ <mat-form-field>
+ <input
+ matInput
+ placeholder="new filename"
+ [(ngModel)]="fileName"
+ />
+ </mat-form-field>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <mat-divider></mat-divider>
+ <div class="sp-dialog-actions">
+ <button
+ mat-button
+ mat-raised-button
+ color="accent"
+ [mat-dialog-close]="fileName"
+ data-cy="sp-file-management-store-file"
+ style="margin-right: 10px"
+ >
+ Rename file
+ </button>
+ <button
+ mat-button
+ mat-raised-button
+ class="mat-basic"
+ (click)="cancel()"
+ style="margin-right: 10px"
+ >
+ Cancel
+ </button>
+ </div>
+</div>
diff --git
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IFileMetadataStorage.java
b/ui/src/app/files/dialog/file-rename/file-rename-dialog.component.scss
similarity index 65%
copy from
streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IFileMetadataStorage.java
copy to ui/src/app/files/dialog/file-rename/file-rename-dialog.component.scss
index 4abaa66c7..704f843e4 100644
---
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IFileMetadataStorage.java
+++ b/ui/src/app/files/dialog/file-rename/file-rename-dialog.component.scss
@@ -15,21 +15,5 @@
* limitations under the License.
*
*/
-package org.apache.streampipes.storage.api;
-import org.apache.streampipes.model.file.FileMetadata;
-
-import java.util.List;
-
-public interface IFileMetadataStorage {
-
- FileMetadata getMetadataById(String id);
-
- List<FileMetadata> getAllFileMetadataDescriptions();
-
- List<FileMetadata> getFilteredFileMetadataDescriptions(String filetype);
-
- void deleteFileMetadata(String id);
-
- void addFileMetadata(FileMetadata fileMetadata);
-}
+@import 'src/scss/sp/sp-dialog';
diff --git
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IFileMetadataStorage.java
b/ui/src/app/files/dialog/file-rename/file-rename-dialog.component.ts
similarity index 58%
copy from
streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IFileMetadataStorage.java
copy to ui/src/app/files/dialog/file-rename/file-rename-dialog.component.ts
index 4abaa66c7..24702ba3a 100644
---
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IFileMetadataStorage.java
+++ b/ui/src/app/files/dialog/file-rename/file-rename-dialog.component.ts
@@ -15,21 +15,23 @@
* limitations under the License.
*
*/
-package org.apache.streampipes.storage.api;
-import org.apache.streampipes.model.file.FileMetadata;
-
-import java.util.List;
-
-public interface IFileMetadataStorage {
-
- FileMetadata getMetadataById(String id);
-
- List<FileMetadata> getAllFileMetadataDescriptions();
-
- List<FileMetadata> getFilteredFileMetadataDescriptions(String filetype);
-
- void deleteFileMetadata(String id);
-
- void addFileMetadata(FileMetadata fileMetadata);
+import { Component, Inject } from '@angular/core';
+import { DialogRef } from '@angular/cdk/dialog';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+
+@Component({
+ selector: 'sp-file-rename-dialog-component',
+ templateUrl: './file-rename-dialog.component.html',
+ styleUrls: ['./file-rename-dialog.component.scss'],
+})
+export class FileRenameDialogComponent {
+ constructor(
+ private dialogRef: DialogRef<FileRenameDialogComponent>,
+ @Inject(MAT_DIALOG_DATA) public fileName: string,
+ ) {}
+
+ cancel() {
+ this.dialogRef.close();
+ }
}
diff --git
a/ui/src/app/files/dialog/file-upload/file-upload-dialog.component.html
b/ui/src/app/files/dialog/file-upload/file-upload-dialog.component.html
index 7fc060395..6d83e5a5c 100644
--- a/ui/src/app/files/dialog/file-upload/file-upload-dialog.component.html
+++ b/ui/src/app/files/dialog/file-upload/file-upload-dialog.component.html
@@ -18,7 +18,7 @@
<div class="sp-dialog-container">
<div class="sp-dialog-content p-15">
- <div fxFlex="100">
+ <div fxFlex="100" *ngIf="this.duplicateFileNames.length === 0">
<div fxFlex="100" style="margin: 5px; width: 100%">
<mat-form-field
style="width: 95%"
@@ -82,10 +82,38 @@
</button>
</div>
</div>
+ <div
+ *ngIf="this.duplicateFileNames.length !== 0"
+ fxLayout="column"
+ style="width: 100%"
+ >
+ <div fxFlex="100" fxLayoutAlign="center" fxLayout="column">
+ <b>
+ <h4>
+ The following files already exist. Please rename them.
+ </h4>
+ </b>
+ <div
+ *ngFor="let name of this.duplicateFileNames; let i = index"
+ >
+ <div class="input-group">
+ <span style="margin-right: 10px">{{ name }}:</span>
+ <mat-form-field>
+ <input
+ matInput
+ placeholder="e.g. {{ '_' + name }}"
+ [(ngModel)]="this.renamedFileNames[i]"
+ />
+ </mat-form-field>
+ </div>
+ </div>
+ </div>
+ </div>
</div>
<mat-divider></mat-divider>
<div class="sp-dialog-actions">
<button
+ *ngIf="this.duplicateFileNames.length === 0"
mat-button
mat-raised-button
color="accent"
@@ -96,6 +124,18 @@
>
Import files
</button>
+
+ <button
+ *ngIf="this.duplicateFileNames.length !== 0"
+ mat-button
+ mat-raised-button
+ color="accent"
+ (click)="renameDuplicateFiles()"
+ [disabled]="fileNames.length === 0"
+ style="margin-right: 10px"
+ >
+ Rename files
+ </button>
<button
mat-button
mat-raised-button
diff --git
a/ui/src/app/files/dialog/file-upload/file-upload-dialog.component.ts
b/ui/src/app/files/dialog/file-upload/file-upload-dialog.component.ts
index d932dbab5..e5c1a8b3f 100644
--- a/ui/src/app/files/dialog/file-upload/file-upload-dialog.component.ts
+++ b/ui/src/app/files/dialog/file-upload/file-upload-dialog.component.ts
@@ -29,6 +29,8 @@ import { FilesService } from '@streampipes/platform-services';
export class FileUploadDialogComponent {
inputValue: string;
fileNames: string[] = [];
+ duplicateFileNames: string[] = [];
+ renamedFileNames: string[] = [];
selectedUploadFiles: FileList;
@@ -56,10 +58,18 @@ export class FileUploadDialogComponent {
}
store() {
- this.uploadStatus = 0;
- if (this.selectedUploadFiles.length > 0) {
- this.uploadFile(0);
- }
+ this.filesService.getAllOriginalFilenames().subscribe(data => {
+ const allFileNames = new Set(data);
+ this.duplicateFileNames = this.fileNames.filter(fileName =>
+ allFileNames.has(fileName.toLowerCase()),
+ );
+ if (this.duplicateFileNames.length === 0) {
+ this.uploadStatus = 0;
+ if (this.selectedUploadFiles.length > 0) {
+ this.uploadFile(0);
+ }
+ }
+ });
}
uploadFile(index: number): void {
@@ -87,4 +97,25 @@ export class FileUploadDialogComponent {
cancel() {
this.dialogRef.close();
}
+
+ renameDuplicateFiles() {
+ const dataTransfer = new DataTransfer();
+ for (let i = 0; i < this.fileNames.length; i++) {
+ let fileName = this.fileNames[i];
+ let index = this.duplicateFileNames.indexOf(fileName);
+ if (index !== -1) {
+ this.fileNames[i] = this.renamedFileNames[index];
+ fileName = this.renamedFileNames[index];
+ }
+ let selectedUploadFile = this.selectedUploadFiles[i];
+ const renamedFile = new File([selectedUploadFile], fileName, {
+ type: selectedUploadFile.type,
+ lastModified: selectedUploadFile.lastModified,
+ });
+ dataTransfer.items.add(renamedFile);
+ }
+ this.selectedUploadFiles = dataTransfer.files;
+ this.duplicateFileNames = [];
+ this.renamedFileNames = [];
+ }
}
diff --git a/ui/src/app/files/files.module.ts b/ui/src/app/files/files.module.ts
index 1569438ac..a93ce7171 100644
--- a/ui/src/app/files/files.module.ts
+++ b/ui/src/app/files/files.module.ts
@@ -41,6 +41,8 @@ import { MatTooltipModule } from '@angular/material/tooltip';
import { PlatformServicesModule } from '@streampipes/platform-services';
import { RouterModule } from '@angular/router';
import { SharedUiModule } from '@streampipes/shared-ui';
+import { FileRenameDialogComponent } from
'./dialog/file-rename/file-rename-dialog.component';
+import { MatDialogModule } from '@angular/material/dialog';
@NgModule({
imports: [
@@ -76,11 +78,13 @@ import { SharedUiModule } from '@streampipes/shared-ui';
],
},
]),
+ MatDialogModule,
],
declarations: [
FilesComponent,
FileOverviewComponent,
FileUploadDialogComponent,
+ FileRenameDialogComponent,
],
providers: [],
})