Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update of PR #2300 #2356

Merged
merged 20 commits into from
Dec 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public ResponseEntity<?> storeFile(@RequestPart("file_upload") MultipartFile fil
FileManager.storeFile(
getAuthenticatedUsername(),
fileDetail.getOriginalFilename(),
fileDetail.getInputStream());
fileDetail.getInputStream()
);
return ok(metadata);
} catch (Exception e) {
return fail();
Expand All @@ -78,7 +79,8 @@ public ResponseEntity<Void> deleteFile(@PathVariable("fileId") String fileId) {
@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));
}

Expand Down Expand Up @@ -119,11 +121,22 @@ public ResponseEntity<byte[]> getFile(
} 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());
}
}
6 changes: 6 additions & 0 deletions streampipes-service-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
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;
Expand All @@ -38,7 +39,8 @@ public List<Migration> getAvailableMigrations() {
new CreateFileAssetTypeMigration(),
new UpdateUsernameViewMigration(),
new AdapterMigration(),
new StoreEmailTemplatesMigration()
new StoreEmailTemplatesMigration(),
new DuplicateFilesRenameMigration()
);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,6 @@ public interface IFileMetadataStorage {
void deleteFileMetadata(String id);

void addFileMetadata(FileMetadata fileMetadata);

void updateFileMetadata(FileMetadata fileMetadata);
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,9 @@ public void deleteFileMetadata(String id) {
public void addFileMetadata(FileMetadata fileMetadata) {
persist(fileMetadata);
}

@Override
public void updateFileMetadata(FileMetadata fileMetadata) {
update(fileMetadata);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,10 @@ export class FilesService {
},
);
}

getAllOriginalFilenames(): Observable<string[]> {
return this.http.get<string[]>(
this.platformServicesCommons.apiBasePath + '/files/allFilenames',
);
}
}
Loading
Loading