Skip to content

Commit 47e2312

Browse files
committed
Merge develop version hash lookups
2 parents 0769e4b + 55cb71d commit 47e2312

6 files changed

Lines changed: 145 additions & 1 deletion

File tree

backend/src/main/java/net/modtale/controller/project/VersionController.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.io.IOException;
66
import java.util.List;
77
import net.modtale.model.dto.project.ManifestInspectionResult;
8+
import net.modtale.model.dto.project.ProjectVersionDTO;
89
import net.modtale.model.dto.request.project.CreateVersionRequest;
910
import net.modtale.model.dto.request.project.UpdateVersionRequest;
1011
import net.modtale.model.dto.response.project.BundleDownloadUrlResponse;
@@ -61,6 +62,26 @@ public ResponseEntity<VersionDependenciesView> getDependencies(
6162
));
6263
}
6364

65+
@GetMapping("/projects/{id}/versions/hash/{hash}")
66+
@PreAuthorize("@apiSecurity.hasProjectPerm(#id, 'PROJECT_READ', authentication)")
67+
public ResponseEntity<ProjectVersionDTO> getVersionByHash(
68+
@PathVariable String id,
69+
@PathVariable String hash,
70+
Authentication authentication
71+
) {
72+
return ResponseEntity.ok(versionApplicationService.getVersionByHash(
73+
id,
74+
hash,
75+
accountService.getCurrentUser(authentication)
76+
));
77+
}
78+
79+
@GetMapping("/version/{hash}")
80+
@PreAuthorize("@apiSecurity.hasAnyPerm('VERSION_READ', authentication)")
81+
public ResponseEntity<ProjectVersionDTO> getVersionByHash(@PathVariable String hash) {
82+
return ResponseEntity.ok(versionApplicationService.getVersionByHash(hash));
83+
}
84+
6485
@PostMapping("/projects/{id}/versions")
6586
@PreAuthorize("@apiSecurity.hasProjectPerm(#id, 'VERSION_CREATE', authentication)")
6687
public ResponseEntity<Void> addVersion(

backend/src/main/java/net/modtale/service/project/access/ProjectVersionAccessService.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,28 @@ public ProjectVersion requireById(
3939
return version;
4040
}
4141

42+
public ProjectVersion findByHash(Project project, String hash) {
43+
if (project == null || project.getVersions() == null || hash == null || hash.isBlank()) {
44+
return null;
45+
}
46+
return project.getVersions().stream()
47+
.filter(version -> version.getHash() != null && version.getHash().equalsIgnoreCase(hash.trim()))
48+
.findFirst()
49+
.orElse(null);
50+
}
51+
52+
public ProjectVersion requireByHash(
53+
Project project,
54+
String hash,
55+
Supplier<? extends RuntimeException> exceptionSupplier
56+
) {
57+
ProjectVersion version = findByHash(project, hash);
58+
if (version == null) {
59+
throw exceptionSupplier.get();
60+
}
61+
return version;
62+
}
63+
4264
public ProjectVersion findByVersionNumber(Project project, String versionNumber, String gameVersion) {
4365
if (project == null || project.getVersions() == null || project.getVersions().isEmpty()) {
4466
return null;

backend/src/main/java/net/modtale/service/project/version/VersionApplicationService.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import net.modtale.mapper.ProjectMapper;
99
import net.modtale.model.dto.project.ManifestInspectionResult;
1010
import net.modtale.model.dto.project.ProjectDependencyDTO;
11+
import net.modtale.model.dto.project.ProjectVersionDTO;
1112
import net.modtale.model.dto.request.project.CreateVersionRequest;
1213
import net.modtale.model.dto.request.project.UpdateVersionRequest;
1314
import net.modtale.model.dto.response.project.BundleDownloadUrlResponse;
@@ -27,17 +28,20 @@ public class VersionApplicationService {
2728

2829
private final VersionMutationApplicationService versionMutationApplicationService;
2930
private final VersionDownloadOrchestrationService versionDownloadOrchestrationService;
31+
private final VersionService versionService;
3032
private final ProjectVersionAccessService projectVersionAccessService;
3133
private final ProjectService projectService;
3234

3335
public VersionApplicationService(
3436
VersionMutationApplicationService versionMutationApplicationService,
3537
VersionDownloadOrchestrationService versionDownloadOrchestrationService,
38+
VersionService versionService,
3639
ProjectVersionAccessService projectVersionAccessService,
3740
ProjectService projectService
3841
) {
3942
this.versionMutationApplicationService = versionMutationApplicationService;
4043
this.versionDownloadOrchestrationService = versionDownloadOrchestrationService;
44+
this.versionService = versionService;
4145
this.projectVersionAccessService = projectVersionAccessService;
4246
this.projectService = projectService;
4347
}
@@ -53,6 +57,22 @@ public VersionDependenciesView getDependencies(String projectId, String versionN
5357
return new VersionDependenciesView(dependencies);
5458
}
5559

60+
public ProjectVersionDTO getVersionByHash(String projectId, String hash, User currentUser) {
61+
Project project = projectService.getProjectVersionsByRouteKey(projectId, currentUser);
62+
if (project == null) {
63+
throw new ResourceNotFoundException("We couldn't find that project.");
64+
}
65+
ProjectVersion version = projectVersionAccessService.requireByHash(project, hash,
66+
() -> new VersionNotFoundException("We couldn't find a version with that file hash for the requested project."));
67+
return ProjectMapper.toVersionDTO(version);
68+
}
69+
70+
public ProjectVersionDTO getVersionByHash(String hash) {
71+
ProjectVersion version = versionService.getVersionByHash(hash)
72+
.orElseThrow(() -> new VersionNotFoundException("We couldn't find a version with that file hash."));
73+
return ProjectMapper.toVersionDTO(version);
74+
}
75+
5676
public void addVersion(String projectId, CreateVersionRequest requestPayload, User currentUser) {
5777
versionMutationApplicationService.addVersion(projectId, requestPayload, currentUser);
5878
}

backend/src/test/java/net/modtale/controller/project/VersionControllerTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import java.time.Instant;
44
import java.util.List;
5+
import java.util.Optional;
56
import net.modtale.config.properties.AppFrontendProperties;
67
import net.modtale.model.dto.request.project.CreateVersionRequest;
8+
import net.modtale.model.dto.project.ProjectVersionDTO;
79
import net.modtale.model.project.Project;
810
import net.modtale.model.project.ProjectClassification;
911
import net.modtale.model.project.ProjectDependency;
@@ -87,6 +89,7 @@ void setUp() {
8789
VersionApplicationService versionApplicationService = new VersionApplicationService(
8890
versionMutationApplicationService,
8991
versionDownloadOrchestrationService,
92+
versionService,
9093
projectVersionAccessService,
9194
projectService
9295
);
@@ -132,6 +135,43 @@ void addVersionSplitsCommaSeparatedDependencyIdsBeforeDelegating() throws Except
132135
);
133136
}
134137

138+
@Test
139+
void getVersionByHashReturnsTheMatchingVersionFromTheRequestedProject() {
140+
User currentUser = user("user-1");
141+
Authentication authentication = mock(Authentication.class);
142+
Project project = project("project-1", "Sky Tools", ProjectClassification.DATA);
143+
ProjectVersion matchingVersion = version("version-2", "2.0.0");
144+
matchingVersion.setHash("abcdef1234");
145+
project.setVersions(List.of(version("version-1", "1.0.0"), matchingVersion));
146+
147+
when(accountService.getCurrentUser(authentication)).thenReturn(currentUser);
148+
when(projectService.getProjectVersionsByRouteKey("project-1", currentUser)).thenReturn(project);
149+
when(projectVersionAccessService.requireByHash(eq(project), eq("ABCDEF1234"), any())).thenReturn(matchingVersion);
150+
151+
var response = controller.getVersionByHash("project-1", "ABCDEF1234", authentication);
152+
153+
assertEquals(200, response.getStatusCode().value());
154+
ProjectVersionDTO body = assertInstanceOf(ProjectVersionDTO.class, response.getBody());
155+
assertEquals("version-2", body.getId());
156+
assertEquals("2.0.0", body.getVersionNumber());
157+
verify(projectService).getProjectVersionsByRouteKey("project-1", currentUser);
158+
}
159+
160+
@Test
161+
void getVersionByHashSearchesGlobally() {
162+
ProjectVersion matchingVersion = version("version-2", "2.0.0");
163+
matchingVersion.setHash("abcdef1234");
164+
when(versionService.getVersionByHash("abcdef1234")).thenReturn(Optional.of(matchingVersion));
165+
166+
var response = controller.getVersionByHash("abcdef1234");
167+
168+
assertEquals(200, response.getStatusCode().value());
169+
ProjectVersionDTO body = assertInstanceOf(ProjectVersionDTO.class, response.getBody());
170+
assertEquals("version-2", body.getId());
171+
assertEquals("2.0.0", body.getVersionNumber());
172+
verify(versionService).getVersionByHash("abcdef1234");
173+
}
174+
135175
@Test
136176
void downloadWithTokenReturnsGeneratedModpackZipAndTracksDependencies() throws Exception {
137177
User currentUser = user("user-1");
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package net.modtale.service.project.access;
2+
3+
import java.util.List;
4+
import net.modtale.exception.VersionNotFoundException;
5+
import net.modtale.model.project.Project;
6+
import net.modtale.model.project.ProjectVersion;
7+
import net.modtale.service.project.validation.ValidationService;
8+
import org.junit.jupiter.api.Test;
9+
10+
import static org.junit.jupiter.api.Assertions.assertEquals;
11+
import static org.junit.jupiter.api.Assertions.assertThrows;
12+
import static org.mockito.Mockito.mock;
13+
14+
class ProjectVersionAccessServiceTest {
15+
16+
private final ProjectVersionAccessService service =
17+
new ProjectVersionAccessService(mock(ValidationService.class));
18+
19+
@Test
20+
void findsAProjectVersionByHashCaseInsensitively() {
21+
ProjectVersion version = new ProjectVersion();
22+
version.setId("version-1");
23+
version.setHash("abcdef1234");
24+
Project project = new Project();
25+
project.setVersions(List.of(version));
26+
27+
assertEquals(version, service.findByHash(project, " ABCDEF1234 "));
28+
}
29+
30+
@Test
31+
void rejectsAHashThatDoesNotBelongToTheProject() {
32+
Project project = new Project();
33+
project.setVersions(List.of());
34+
35+
assertThrows(VersionNotFoundException.class, () -> service.requireByHash(
36+
project,
37+
"missing",
38+
() -> new VersionNotFoundException("missing")
39+
));
40+
}
41+
}

frontend/src/modules/core/views/ApiDocs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ const endpointSpecificExample = (method: string, path: string, code: string): un
768768
expiresIn: 300,
769769
};
770770
}
771-
if (path === '/api/v1/version/{hash}' && code === '200') {
771+
if ((path === '/api/v1/projects/{id}/versions/hash/{hash}' || path === '/api/v1/version/{hash}') && code === '200') {
772772
return {
773773
id: sampleVersionSummary.id,
774774
versionNumber: sampleVersionSummary.versionNumber,

0 commit comments

Comments
 (0)