Skip to content

Commit 50a916a

Browse files
committed
Refactor project key existence checks: introduce ProjectExistenceService, update key generation and validation logic, and remove redundant exception handler
1 parent e4641bd commit 50a916a

12 files changed

Lines changed: 315 additions & 222 deletions

File tree

api-project/src/main/java/org/opendevstack/apiservice/project/controller/advice/ProjectExceptionHandler.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import org.opendevstack.apiservice.project.exception.ClientAppNotRegisteredException;
77
import org.opendevstack.apiservice.project.exception.ErrorKey;
88
import org.opendevstack.apiservice.project.exception.ProjectCreationException;
9-
import org.opendevstack.apiservice.project.exception.ProjectKeyGenerationException;
109
import org.opendevstack.apiservice.project.exception.ProjectValidationException;
1110
import org.opendevstack.apiservice.project.model.CreateProjectResponse;
1211
import org.springframework.http.HttpStatus;
@@ -97,18 +96,6 @@ public ResponseEntity<CreateProjectResponse> handleProjectCreationException(
9796
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
9897
}
9998

100-
@ExceptionHandler(ProjectKeyGenerationException.class)
101-
public ResponseEntity<CreateProjectResponse> handleProjectKeyGenerationException(
102-
ProjectKeyGenerationException ex) {
103-
log.error("Failed to generate project key: {}", ex.getMessage(), ex);
104-
CreateProjectResponse response = new CreateProjectResponse();
105-
response.setLocation(ProjectController.API_BASE_PATH);
106-
response.setError(ErrorKey.INTERNAL_ERROR.getMessage());
107-
response.setErrorKey("PROJECT_KEY_GENERATION_FAILED");
108-
response.setMessage("Failed to generate a unique project key.");
109-
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
110-
}
111-
11299
@ExceptionHandler(AutomationPlatformException.class)
113100
public ResponseEntity<CreateProjectResponse> handleAutomationPlatformException(
114101
AutomationPlatformException ex) {

api-project/src/main/java/org/opendevstack/apiservice/project/exception/ProjectKeyGenerationException.java

Lines changed: 0 additions & 12 deletions
This file was deleted.

api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilder.java

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,32 @@
11
package org.opendevstack.apiservice.project.facade.impl;
22

3-
import java.util.Arrays;
4-
import java.util.List;
5-
import java.util.UUID;
63
import lombok.RequiredArgsConstructor;
74
import lombok.extern.slf4j.Slf4j;
85
import org.apache.logging.log4j.util.Strings;
96
import org.opendevstack.apiservice.persistence.entity.ClientAppEntity;
107
import org.opendevstack.apiservice.persistence.entity.ClientAppProjectFlavorEntity;
118
import org.opendevstack.apiservice.project.exception.ErrorKey;
12-
import org.opendevstack.apiservice.project.exception.ProjectKeyGenerationException;
9+
import org.opendevstack.apiservice.project.exception.ProjectCreationException;
1310
import org.opendevstack.apiservice.project.exception.ProjectValidationException;
1411
import org.opendevstack.apiservice.project.model.CreateProjectRequest;
12+
import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException;
13+
import org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException;
1514
import org.opendevstack.apiservice.serviceproject.service.GenerateProjectKeyService;
16-
import org.opendevstack.apiservice.serviceproject.service.ProjectService;
15+
import org.opendevstack.apiservice.serviceproject.service.ProjectExistenceService;
1716
import org.springframework.stereotype.Component;
1817

18+
import java.util.Arrays;
19+
import java.util.List;
20+
import java.util.UUID;
21+
1922
@Component
2023
@RequiredArgsConstructor
2124
@Slf4j
2225
public class ProjectCreationCommandBuilder {
23-
24-
private final ProjectService projectService;
2526

2627
private final GenerateProjectKeyService generateProjectKeyService;
28+
29+
private final ProjectExistenceService projectExistenceService;
2730

2831
public ProjectCreationCommand build(CreateProjectRequest request, ClientAppEntity clientApp) {
2932
ClientAppProjectFlavorEntity flavor = resolveFlavor(request, clientApp);
@@ -100,25 +103,22 @@ private ClientAppProjectFlavorEntity resolveByConfigurationItem(
100103
}
101104

102105
private String resolveProjectKey(String existingProjectKey, ClientAppProjectFlavorEntity flavor) {
103-
if (Strings.isNotEmpty(existingProjectKey)) {
104-
validateProjectNotExists(existingProjectKey);
105-
return existingProjectKey;
106-
}
107-
108-
String pattern = flavor.getProjectKeyPattern();
109-
110106
try {
111-
String generatedProjectKey = generateProjectKeyService.generateProjectKey(pattern);
112-
validateProjectNotExists(generatedProjectKey);
113-
return generatedProjectKey;
114-
} catch (org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException e) {
115-
throw new ProjectKeyGenerationException("Failed to generate unique project key", e);
116-
}
117-
}
118-
119-
private void validateProjectNotExists(String projectKey) {
120-
if (projectService.getProject(projectKey) != null) {
121-
throw new ProjectValidationException(ErrorKey.DUPLICATE_RECORD);
107+
if (Strings.isNotEmpty(existingProjectKey)) {
108+
if (!projectExistenceService.isProjectFound(existingProjectKey)) {
109+
return existingProjectKey;
110+
}
111+
112+
throw new ProjectValidationException(ErrorKey.PROJECT_ALREADY_EXISTS);
113+
}
114+
115+
String pattern = flavor.getProjectKeyPattern();
116+
117+
return generateProjectKeyService.generateProjectKey(pattern);
118+
} catch (ProjectKeyGenerationException e) {
119+
throw new ProjectCreationException("Error generating the project key", e);
120+
} catch (ProjectExistenceServiceException e) {
121+
throw new ProjectCreationException("Error checking if the generated key exists: " + e.getMessage(), e);
122122
}
123123
}
124124

api-project/src/test/java/org/opendevstack/apiservice/project/controller/advice/ProjectExceptionHandlerTest.java

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import org.opendevstack.apiservice.project.exception.ClientAppNotRegisteredException;
1313
import org.opendevstack.apiservice.project.exception.ErrorKey;
1414
import org.opendevstack.apiservice.project.exception.ProjectCreationException;
15-
import org.opendevstack.apiservice.project.exception.ProjectKeyGenerationException;
1615
import org.opendevstack.apiservice.project.exception.ProjectValidationException;
1716
import org.opendevstack.apiservice.project.model.CreateProjectRequest;
1817
import org.opendevstack.apiservice.project.model.CreateProjectResponse;
@@ -26,9 +25,9 @@
2625
import java.util.stream.Stream;
2726

2827
import static org.junit.jupiter.api.Assertions.assertEquals;
29-
import static org.junit.jupiter.api.Assertions.fail;
3028
import static org.junit.jupiter.api.Assertions.assertNotNull;
3129
import static org.junit.jupiter.api.Assertions.assertNull;
30+
import static org.junit.jupiter.api.Assertions.fail;
3231

3332
class ProjectExceptionHandlerTest {
3433

@@ -220,24 +219,6 @@ void handle_project_creation_exception_returns_conflict() {
220219
assertNull(result.getBody().getErrorDescription());
221220
}
222221

223-
@Test
224-
void handle_project_key_generation_exception_returns_internal_server_error() {
225-
ProjectKeyGenerationException exception = new ProjectKeyGenerationException(
226-
"Failed to generate unique project key after 10 retries");
227-
228-
ResponseEntity<CreateProjectResponse> result = sut.handleProjectKeyGenerationException(exception);
229-
230-
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, result.getStatusCode());
231-
assertNotNull(result.getBody());
232-
assertEquals(ProjectController.API_BASE_PATH, result.getBody().getLocation());
233-
assertEquals("Internal error", result.getBody().getError());
234-
assertEquals("PROJECT_KEY_GENERATION_FAILED", result.getBody().getErrorKey());
235-
assertEquals("Failed to generate a unique project key.", result.getBody().getMessage());
236-
assertNull(result.getBody().getProjectKey());
237-
assertNull(result.getBody().getStatus());
238-
assertNull(result.getBody().getErrorDescription());
239-
}
240-
241222
@Test
242223
void handle_client_app_not_registered_exception_returns_forbidden() {
243224
ClientAppNotRegisteredException exception = new ClientAppNotRegisteredException("client-123");

api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilderTest.java

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
package org.opendevstack.apiservice.project.facade.impl;
22

3-
import static org.junit.jupiter.api.Assertions.assertEquals;
4-
import static org.junit.jupiter.api.Assertions.assertThrows;
5-
import static org.mockito.Mockito.verify;
6-
import static org.mockito.Mockito.when;
7-
8-
import java.util.List;
9-
import java.util.UUID;
103
import org.junit.jupiter.api.AfterEach;
114
import org.junit.jupiter.api.BeforeEach;
125
import org.junit.jupiter.api.Test;
@@ -15,23 +8,30 @@
158
import org.opendevstack.apiservice.persistence.entity.ClientAppEntity;
169
import org.opendevstack.apiservice.persistence.entity.ClientAppProjectFlavorEntity;
1710
import org.opendevstack.apiservice.project.exception.ErrorKey;
18-
import org.opendevstack.apiservice.project.exception.ProjectKeyGenerationException;
11+
import org.opendevstack.apiservice.project.exception.ProjectCreationException;
1912
import org.opendevstack.apiservice.project.exception.ProjectValidationException;
2013
import org.opendevstack.apiservice.project.model.CreateProjectRequest;
21-
import org.opendevstack.apiservice.serviceproject.model.ProjectResponse;
22-
import org.opendevstack.apiservice.serviceproject.model.Status;
14+
import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException;
2315
import org.opendevstack.apiservice.serviceproject.service.GenerateProjectKeyService;
24-
import org.opendevstack.apiservice.serviceproject.service.ProjectService;
16+
import org.opendevstack.apiservice.serviceproject.service.ProjectExistenceService;
17+
18+
import java.util.List;
19+
import java.util.UUID;
20+
21+
import static org.junit.jupiter.api.Assertions.assertEquals;
22+
import static org.junit.jupiter.api.Assertions.assertThrows;
23+
import static org.mockito.Mockito.verify;
24+
import static org.mockito.Mockito.when;
2525

2626
class ProjectCreationCommandBuilderTest {
2727

2828
private static final UUID CLIENT_ID = UUID.fromString("00000000-0000-0000-0000-000000000001");
2929

30-
@Mock
31-
private ProjectService projectService;
32-
3330
@Mock
3431
private GenerateProjectKeyService generateProjectKeyService;
32+
33+
@Mock
34+
private ProjectExistenceService projectExistenceService;
3535

3636
private ProjectCreationCommandBuilder sut;
3737

@@ -40,7 +40,7 @@ class ProjectCreationCommandBuilderTest {
4040
@BeforeEach
4141
void set_up() {
4242
mocks = MockitoAnnotations.openMocks(this);
43-
sut = new ProjectCreationCommandBuilder(projectService, generateProjectKeyService);
43+
sut = new ProjectCreationCommandBuilder(generateProjectKeyService, projectExistenceService);
4444
}
4545

4646
@AfterEach
@@ -49,14 +49,14 @@ void tear_down() throws Exception {
4949
}
5050

5151
@Test
52-
void build_resolves_defaults_from_flavor_when_request_fields_are_missing() {
52+
void build_resolves_defaults_from_flavor_when_request_fields_are_missing() throws ProjectExistenceServiceException {
5353
ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1");
5454
ClientAppEntity clientApp = build_client_app(List.of(flavor));
5555
CreateProjectRequest request = build_request("DLSS", null, "KEY01");
5656
request.setOwner(null);
5757
request.setLocation(null);
5858

59-
when(projectService.getProject("KEY01")).thenReturn(null);
59+
when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false);
6060

6161
ProjectCreationCommand result = sut.build(request, clientApp);
6262

@@ -68,12 +68,12 @@ void build_resolves_defaults_from_flavor_when_request_fields_are_missing() {
6868
}
6969

7070
@Test
71-
void build_resolves_flavor_from_configuration_item_when_flavor_is_not_provided() {
71+
void build_resolves_flavor_from_configuration_item_when_flavor_is_not_provided() throws ProjectExistenceServiceException {
7272
ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1");
7373
ClientAppEntity clientApp = build_client_app(List.of(flavor));
7474
CreateProjectRequest request = build_request(null, "CI-001", "KEY01");
7575

76-
when(projectService.getProject("KEY01")).thenReturn(null);
76+
when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false);
7777

7878
ProjectCreationCommand result = sut.build(request, clientApp);
7979

@@ -82,13 +82,13 @@ void build_resolves_flavor_from_configuration_item_when_flavor_is_not_provided()
8282
}
8383

8484
@Test
85-
void build_generates_project_key_when_request_project_key_is_null() throws Exception {
85+
void build_generates_project_key_when_request_project_key_is_null() throws ProjectExistenceServiceException, org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException {
8686
ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1");
8787
ClientAppEntity clientApp = build_client_app(List.of(flavor));
8888
CreateProjectRequest request = build_request("DLSS", null, null);
8989

9090
when(generateProjectKeyService.generateProjectKey("DLSS%06d")).thenReturn("DLSS000001");
91-
when(projectService.getProject("DLSS000001")).thenReturn(null);
91+
when(projectExistenceService.isProjectFound("DLSS000001")).thenReturn(false);
9292

9393
ProjectCreationCommand result = sut.build(request, clientApp);
9494

@@ -97,19 +97,16 @@ void build_generates_project_key_when_request_project_key_is_null() throws Excep
9797
}
9898

9999
@Test
100-
void build_throws_validation_exception_when_project_key_already_exists() {
100+
void build_throws_validation_exception_when_project_key_already_exists() throws ProjectExistenceServiceException {
101101
ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1");
102102
ClientAppEntity clientApp = build_client_app(List.of(flavor));
103103
CreateProjectRequest request = build_request("DLSS", null, "KEY01");
104104

105-
when(projectService.getProject("KEY01")).thenReturn(ProjectResponse.builder()
106-
.projectKey("KEY01")
107-
.status(Status.RUNNING)
108-
.build());
105+
when(projectExistenceService.isProjectFound("KEY01")).thenReturn(true);
109106

110107
ProjectValidationException ex = assertThrows(ProjectValidationException.class,
111108
() -> sut.build(request, clientApp));
112-
assertEquals(ErrorKey.DUPLICATE_RECORD, ex.getErrorKey());
109+
assertEquals(ErrorKey.PROJECT_ALREADY_EXISTS, ex.getErrorKey());
113110
}
114111

115112
@Test
@@ -144,7 +141,7 @@ void build_throws_project_key_generation_exception_when_generation_fails() throw
144141
when(generateProjectKeyService.generateProjectKey("DLSS%06d"))
145142
.thenThrow(new org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException("fail"));
146143

147-
assertThrows(ProjectKeyGenerationException.class, () -> sut.build(request, clientApp));
144+
assertThrows(ProjectCreationException.class, () -> sut.build(request, clientApp));
148145
}
149146

150147
private CreateProjectRequest build_request(String flavor, String configItem, String projectKey) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.opendevstack.apiservice.serviceproject.exception;
2+
3+
public class ProjectExistenceServiceException extends Exception {
4+
5+
public ProjectExistenceServiceException(String message) {
6+
super(message);
7+
}
8+
9+
public ProjectExistenceServiceException(String message, Throwable cause) {
10+
super(message, cause);
11+
}
12+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package org.opendevstack.apiservice.serviceproject.service;
22

3+
import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException;
34
import org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException;
45

56
public interface GenerateProjectKeyService {
67

78
String DEFAULT_PROJECT_KEY_PATTERN = "SS%06d";
89

9-
String generateProjectKey(String projectKeyPattern) throws ProjectKeyGenerationException;
10+
String generateProjectKey(String projectKeyPattern) throws ProjectKeyGenerationException, ProjectExistenceServiceException;
1011
}
1112

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.opendevstack.apiservice.serviceproject.service;
2+
3+
import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException;
4+
5+
public interface ProjectExistenceService {
6+
7+
boolean isProjectFound(String projectKey) throws ProjectExistenceServiceException;
8+
}

0 commit comments

Comments
 (0)