Skip to content
This repository was archived by the owner on Apr 5, 2024. It is now read-only.

Commit e54ebf0

Browse files
FF-444 - Search Endpoint (#121)
* basic function skeleton * Wrote Logic * FF-447 add search integration tests * added url decoding when searching. Co-authored-by: qvalentin <[email protected]>
1 parent c1bdc68 commit e54ebf0

File tree

9 files changed

+182
-8
lines changed

9 files changed

+182
-8
lines changed

src/main/java/de/filefighter/rest/domain/filesystem/business/FileSystemBusinessService.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package de.filefighter.rest.domain.filesystem.business;
22

3+
import de.filefighter.rest.configuration.RestConfiguration;
34
import de.filefighter.rest.domain.common.Pair;
45
import de.filefighter.rest.domain.common.exceptions.FileFighterDataException;
56
import de.filefighter.rest.domain.filesystem.data.InteractionType;
@@ -260,4 +261,49 @@ public Pair<List<FileSystemItem>, String> downloadFileSystemEntity(List<Long> id
260261
}
261262
return new Pair<>(returnList, zipName);
262263
}
264+
265+
public List<FileSystemItem> searchFileSystemEntity(String sanitizedSearch, User authenticatedUser) {
266+
// check for username with the same name
267+
User userWithTheName;
268+
List<FileSystemEntity> foundEntities = fileSystemRepository.findAllByNameContainingIgnoreCase(sanitizedSearch);
269+
270+
try {
271+
userWithTheName = userBusinessService.findUserByUsername(sanitizedSearch);
272+
273+
if (userWithTheName.getUserId() != RestConfiguration.RUNTIME_USER_ID) {
274+
// there is a user with the name -> add the users root to list.
275+
foundEntities.add(fileSystemHelperService.getRootEntityForUser(userWithTheName));
276+
}
277+
} catch (UserNotFoundException ignored) {
278+
log.debug("Searched for {}, was not a username.", sanitizedSearch);
279+
}
280+
281+
if (null == foundEntities)
282+
throw new FileSystemItemNotFoundException();
283+
284+
List<FileSystemEntity> visibleEntities = foundEntities.stream()
285+
.filter(entity -> fileSystemHelperService.userIsAllowedToInteractWithFileSystemEntity(entity, authenticatedUser, InteractionType.READ))
286+
.collect(Collectors.toList());
287+
288+
return visibleEntities.stream()
289+
.map(entity -> {
290+
String username = fileSystemHelperService.getOwnerUsernameForEntity(entity);
291+
String path;
292+
if (entity.isFile() || entity.getTypeId() != FileSystemType.FOLDER.getId()) {
293+
FileSystemEntity parent = fileSystemRepository.findByItemIdsContaining(entity.getFileSystemId());
294+
if (null == parent)
295+
throw new FileFighterDataException("Couldn't find parent entity for id: " + entity.getFileSystemId());
296+
path = parent.getPath();
297+
if (path.equals("/")) {
298+
path += entity.getName();
299+
} else {
300+
path += "/" + entity.getName();
301+
}
302+
} else {
303+
path = entity.getPath();
304+
}
305+
return fileSystemHelperService.createDTO(entity, authenticatedUser, "/" + username + path);
306+
})
307+
.collect(Collectors.toList());
308+
}
263309
}

src/main/java/de/filefighter/rest/domain/filesystem/business/FileSystemHelperService.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.time.Instant;
2626
import java.util.*;
2727
import java.util.function.Function;
28+
import java.util.stream.Collectors;
2829

2930
import static de.filefighter.rest.domain.filesystem.business.FileSystemBusinessService.DELETION_FAILED_MSG;
3031

@@ -410,7 +411,7 @@ public Function<FileSystemEntity, String> getParentNameEntity() {
410411
};
411412
}
412413

413-
private String getOwnerUsernameForEntity(FileSystemEntity entity) {
414+
public String getOwnerUsernameForEntity(FileSystemEntity entity) {
414415
User owner;
415416
try {
416417
owner = userBusinessService.findUserById(entity.getOwnerId());
@@ -420,6 +421,21 @@ private String getOwnerUsernameForEntity(FileSystemEntity entity) {
420421
return owner.getUsername();
421422
}
422423

424+
public FileSystemEntity getRootEntityForUser(User userWithTheName) {
425+
List<FileSystemEntity> rootForUser = fileSystemRepository.findByPath("/")
426+
.stream()
427+
.filter(entity -> entity.getOwnerId() == userWithTheName.getUserId())
428+
.collect(Collectors.toList());
429+
430+
if (rootForUser.isEmpty())
431+
throw new FileFighterDataException("Found not root folder for user with id: " + userWithTheName.getUserId());
432+
433+
if (rootForUser.size() > 1)
434+
throw new FileFighterDataException("Found more than one root folder for user with id: " + userWithTheName.getUserId());
435+
436+
return rootForUser.get(0);
437+
}
438+
423439
public double getTotalFileSize() {
424440
List<FileSystemEntity> entities = fileSystemRepository.findByPath("/");
425441
if (null == entities)

src/main/java/de/filefighter/rest/domain/filesystem/data/persistence/FileSystemRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public interface FileSystemRepository extends MongoRepository<FileSystemEntity,
1919

2020
List<FileSystemEntity> findAllByFileSystemIdInAndNameIgnoreCase(List<Long> fileSystemId, String name);
2121

22+
List<FileSystemEntity> findAllByNameContainingIgnoreCase(String name);
23+
2224
// this does work tho.
2325
FileSystemEntity findByItemIdsContaining(long id);
2426

src/main/java/de/filefighter/rest/domain/filesystem/rest/FileSystemRestController.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import org.springframework.http.ResponseEntity;
1212
import org.springframework.web.bind.annotation.*;
1313

14+
import java.net.URLDecoder;
15+
import java.nio.charset.StandardCharsets;
1416
import java.util.List;
1517

1618
import static de.filefighter.rest.configuration.RestConfiguration.*;
@@ -48,12 +50,12 @@ public ResponseEntity<FileSystemItem> getFileOrFolderInfo(
4850
}
4951

5052
@GetMapping(FS_BASE_URI + "search")
51-
public ResponseEntity<FileSystemItem> searchFileOrFolderByName(
53+
public ResponseEntity<List<FileSystemItem>> searchFileOrFolderByName(
5254
@RequestParam(name = "name", defaultValue = "name") String name,
5355
@RequestHeader(value = "Authorization") String accessToken
5456
) {
5557

56-
log.info("Searching for file or folder with name {}", name);
58+
log.info("Searching for file or folder with name {} decoded: ({})", name, URLDecoder.decode(name, StandardCharsets.UTF_8));
5759
return fileSystemRestService.findFileOrFolderByNameAndAccessToken(name, accessToken);
5860
}
5961

src/main/java/de/filefighter/rest/domain/filesystem/rest/FileSystemRestService.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import org.springframework.http.ResponseEntity;
1818
import org.springframework.stereotype.Service;
1919

20+
import java.net.URLDecoder;
21+
import java.nio.charset.StandardCharsets;
2022
import java.util.List;
2123

2224
@Service
@@ -96,8 +98,12 @@ public ResponseEntity<List<FileSystemItem>> deleteFileSystemItemWithIdAndAccessT
9698
}
9799

98100
@Override
99-
public ResponseEntity<FileSystemItem> findFileOrFolderByNameAndAccessToken(String name, String accessToken) {
100-
return null;
101+
public ResponseEntity<List<FileSystemItem>> findFileOrFolderByNameAndAccessToken(String name, String accessToken) {
102+
User authenticatedUser = authenticationService.bearerAuthenticationWithAccessToken(accessToken);
103+
String sanitizedSearch = inputSanitizerService.sanitizeString(name);
104+
sanitizedSearch = URLDecoder.decode(sanitizedSearch, StandardCharsets.UTF_8);
105+
106+
return new ResponseEntity<>(fileSystemBusinessService.searchFileSystemEntity(sanitizedSearch, authenticatedUser), HttpStatus.OK);
101107
}
102108

103109
@Override

src/main/java/de/filefighter/rest/domain/filesystem/rest/FileSystemRestServiceInterface.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public interface FileSystemRestServiceInterface {
1515

1616
ResponseEntity<FileSystemItem> getInfoAboutFileOrFolderByIdAndAccessToken(long fsItemId, String accessToken);
1717

18-
ResponseEntity<FileSystemItem> findFileOrFolderByNameAndAccessToken(String name, String accessToken);
18+
ResponseEntity<List<FileSystemItem>> findFileOrFolderByNameAndAccessToken(String name, String accessToken);
1919

2020
ResponseEntity<List<FileSystemItem>> uploadFileSystemItemWithAccessToken(long rootItemId, FileSystemUpload fileSystemUpload, String accessToken);
2121

src/test/java/de/filefighter/rest/cucumber/CrudFileSystemSteps.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import io.cucumber.java.en.When;
55
import org.springframework.http.HttpMethod;
66

7+
import java.io.UnsupportedEncodingException;
8+
import java.net.URLEncoder;
9+
import java.nio.charset.StandardCharsets;
710
import java.util.HashMap;
811

912
import static de.filefighter.rest.configuration.RestConfiguration.*;
@@ -41,4 +44,14 @@ public void userWithTokenWantsToGetTheInfoOfFileSystemItemWithTheFileSystemId(St
4144

4245
}
4346

47+
@When("user with token {string} searches for {string}")
48+
public void userWithTokenSearchesFor(String accessTokenValue, String searchValue) throws UnsupportedEncodingException {
49+
50+
String authHeaderString = AUTHORIZATION_BEARER_PREFIX + accessTokenValue;
51+
52+
HashMap<String, String> authHeader = new HashMap<>();
53+
authHeader.put("Authorization", authHeaderString);
54+
55+
executeRestApiCall(HttpMethod.GET, BASE_API_URI + FS_BASE_URI + "/search?name="+ URLEncoder.encode(searchValue, StandardCharsets.UTF_8.toString()), authHeader);
56+
}
4457
}

src/test/java/de/filefighter/rest/domain/filesystem/rest/FileSystemRestControllerUnitTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,16 @@ void getFileOrFolderInfo() {
5959
@Test
6060
void searchFileOrFolderByName() {
6161
FileSystemItem file = FileSystemItem.builder().build();
62-
ResponseEntity<FileSystemItem> expectedModel = new ResponseEntity<>(file, OK);
62+
List<FileSystemItem> list = new ArrayList<>();
63+
list.add(file);
64+
ResponseEntity<List<FileSystemItem>> expectedModel = new ResponseEntity<>(list, OK);
6365

6466
String name = "randomFile.exe";
6567
String token = "token";
6668

6769
when(fileSystemRestServiceMock.findFileOrFolderByNameAndAccessToken(name, token)).thenReturn(expectedModel);
6870

69-
ResponseEntity<FileSystemItem> actualModel = fileSystemRestController.searchFileOrFolderByName(name, token);
71+
ResponseEntity<List<FileSystemItem>> actualModel = fileSystemRestController.searchFileOrFolderByName(name, token);
7072
assertEquals(expectedModel, actualModel);
7173
}
7274

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
Feature: Search Files and Directories
2+
As a user
3+
I want to be able to search for files or folders by their name.
4+
5+
Background:
6+
Given database is empty
7+
And runtime user exists
8+
And user with userId 1234 exists and has username "Richard", password "badPassword"
9+
And user with userId 420 exists and has username "Nasir", password "AlsoBadPassword"
10+
And accessToken with value "Richard" exists for user 1234
11+
And accessToken with value "Nasir" exists for user 420
12+
And user with userId 1234 has HomeFolder with Id 1234
13+
And user with userId 420 has HomeFolder with Id 420
14+
And fileSystemItem with the fileSystemId 42 exists, has owner with userId 1234 has the path "/bla" and name "bla"
15+
And fileSystemItem with the fileSystemId 72 exists, has owner with userId 1234 and name "wow.txt"
16+
And fileSystemItem with the fileSystemId 42 is a folder and contains the fileSystemId 72
17+
18+
Scenario: Successful interaction, found file in personal folder
19+
When user with token "Richard" searches for "wow"
20+
Then response status code is 200
21+
And the response contains a entity with the path "/richard/bla/wow.txt" that has key "name" with value "wow.txt"
22+
23+
Scenario: Successful interaction, found file in personal folder, ignore case
24+
When user with token "Richard" searches for "WOW.TXT"
25+
Then response status code is 200
26+
And the response contains a entity with the path "/richard/bla/wow.txt" that has key "name" with value "wow.txt"
27+
28+
Scenario: Successful interaction, found folder in personal folder
29+
When user with token "Richard" searches for "bla"
30+
Then response status code is 200
31+
And the response contains a entity with the path "/richard/bla" that has key "fileSystemId" with value "42"
32+
33+
Scenario: Successful interaction, found files in personal folder by half the name
34+
Given fileSystemItem with the fileSystemId 73 exists, has owner with userId 1234 and name "SuchAlongFileName.txt"
35+
And fileSystemItem with the fileSystemId 42 is a folder and contains the fileSystemId 73
36+
And fileSystemItem with the fileSystemId 74 exists, has owner with userId 1234 and name "AnotherlongFileName.xml"
37+
And fileSystemItem with the fileSystemId 42 is a folder and contains the fileSystemId 74
38+
When user with token "Richard" searches for "FileName"
39+
Then response status code is 200
40+
And the response contains a entity with the path "/richard/bla/suchalongfilename.txt" that has key "fileSystemId" with value "73"
41+
And the response contains a entity with the path "/richard/bla/anotherlongfilename.xml" that has key "fileSystemId" with value "74"
42+
43+
Scenario: nothing found
44+
When user with token "Richard" searches for "ugabuga"
45+
Then response status code is 200
46+
And the response contains an empty list for files and folders
47+
48+
Scenario: no permissions
49+
When user with token "Nasir" searches for "wow"
50+
Then response status code is 200
51+
And the response contains an empty list for files and folders
52+
53+
Scenario: Successful interaction, found file in shared folder
54+
Given user with the userId 420 is allowed to VIEW the fileSystemItem with the fileSystemId 42
55+
And user with the userId 420 is allowed to VIEW the fileSystemItem with the fileSystemId 72
56+
When user with token "Nasir" searches for "wow"
57+
Then response status code is 200
58+
And the response contains a entity with the path "/richard/bla/wow.txt" that has key "name" with value "wow.txt"
59+
60+
Scenario: Successful interaction, found folder in personal folder
61+
Given user with the userId 420 is allowed to VIEW the fileSystemItem with the fileSystemId 42
62+
And user with the userId 420 is allowed to VIEW the fileSystemItem with the fileSystemId 72
63+
When user with token "Richard" searches for "bla"
64+
Then response status code is 200
65+
And the response contains a entity with the path "/richard/bla" that has key "fileSystemId" with value "42"
66+
67+
68+
69+
Scenario Outline: Search for <filename> with <search>
70+
Given fileSystemItem with the fileSystemId 73 exists, has owner with userId 1234 and name "<filename>"
71+
And fileSystemItem with the fileSystemId 42 is a folder and contains the fileSystemId 73
72+
When user with token "Richard" searches for "<search>"
73+
Then response status code is 200
74+
And the response contains the file with fileSystemId 73 and name "<filename>"
75+
76+
Examples:
77+
|filename |search |
78+
|HansWurst |st |
79+
|Esel.txt |.txt |
80+
|HinterhältigesWiesel.wav |Hältiges |
81+
|HilfeHilfe |hilfe |
82+
|DuDummeSau |du |
83+
|NichtDasNagetier |Tier |
84+
|DuBauer |auer |
85+
|DasIstWahreMacht |d |
86+
|DIesESMALWerdeICHEuchBesiegen |DiesesMalWerdeIchEuchBesiegen |
87+
|filename |filename |

0 commit comments

Comments
 (0)