From 225e512169f7e7e92ff5348be9ae1dfdc10e5282 Mon Sep 17 00:00:00 2001 From: Joselee2908 Date: Wed, 13 May 2026 17:39:39 +0200 Subject: [PATCH 1/3] fix(api-project): fixed registration to only check collection for already existence --- .../impl/ProjectCreationCommandBuilder.java | 355 +++++++------- .../facade/impl/ProjectsFacadeImpl.java | 245 +++++----- .../ProjectCreationCommandBuilderTest.java | 448 ++++++++++-------- .../facade/impl/ProjectsFacadeImplTest.java | 422 ++++++++--------- .../service/ProjectExistenceService.java | 22 +- .../impl/ProjectExistenceServiceImpl.java | 189 ++++---- 6 files changed, 876 insertions(+), 805 deletions(-) diff --git a/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilder.java b/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilder.java index 15866ca..46eab72 100644 --- a/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilder.java +++ b/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilder.java @@ -1,162 +1,193 @@ -package org.opendevstack.apiservice.project.facade.impl; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.logging.log4j.util.Strings; -import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; -import org.opendevstack.apiservice.persistence.entity.ClientAppProjectFlavorEntity; -import org.opendevstack.apiservice.project.exception.ErrorKey; -import org.opendevstack.apiservice.project.exception.ProjectAlreadyExistsException; -import org.opendevstack.apiservice.project.exception.ProjectCreationException; -import org.opendevstack.apiservice.project.exception.ProjectValidationException; -import org.opendevstack.apiservice.project.model.CreateProjectRequest; -import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; -import org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException; -import org.opendevstack.apiservice.serviceproject.service.GenerateProjectKeyService; -import org.opendevstack.apiservice.serviceproject.service.ProjectExistenceService; -import org.springframework.stereotype.Component; - -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; - -@Component -@RequiredArgsConstructor -@Slf4j -public class ProjectCreationCommandBuilder { - - private final GenerateProjectKeyService generateProjectKeyService; - - private final ProjectExistenceService projectExistenceService; - - public ProjectCreationCommand build(CreateProjectRequest request, ClientAppEntity clientApp) { - ClientAppProjectFlavorEntity flavor = resolveFlavor(request, clientApp); - - String projectFlavor = firstNonBlank(request.getProjectFlavor(), flavor.getName()); - String configurationItem = firstNonBlank(request.getConfigurationItem(), flavor.getConfigItem()); - String owner = firstNonBlank(request.getOwner(), flavor.getProjectOwner()); - String x2account = firstNonBlank(request.getX2OdsAccount(), flavor.getServiceAccount()); - String location = firstNonBlank(request.getLocation(), flavor.getLocation()); - String projectKey = resolveProjectKey(request.getProjectKey(), flavor); - String projectName = resolveProjectName(request.getProjectName(), projectKey); - String projectDescription = firstNonBlank(request.getProjectDescription(), "project " + projectFlavor); - - return new ProjectCreationCommand( - projectKey, - projectName, - projectDescription, - projectFlavor, - configurationItem, - location, - x2account, - owner, - clientApp.getId()); - } - - private ClientAppProjectFlavorEntity resolveFlavor(CreateProjectRequest request, ClientAppEntity clientApp) { - List flavors = clientApp.getProjectFlavors(); - - if (flavors == null || flavors.isEmpty()) { - log.warn("ClientApp '{}' has no project flavors configured", clientApp.getClientId()); - throw new ProjectValidationException(ErrorKey.INVALID_PROJECT_FLAVOR); - } - - if (Strings.isNotEmpty(request.getProjectFlavor())) { - return resolveByFlavorName(request.getProjectFlavor(), flavors, clientApp.getClientId()); - } - - if (Strings.isNotEmpty(request.getConfigurationItem())) { - return resolveByConfigurationItem(request.getConfigurationItem(), flavors, clientApp.getClientId()); - } - - throw new ProjectValidationException(ErrorKey.BAD_REQUEST_FLAVOR_CONFIG_ITEM); - } - - private ClientAppProjectFlavorEntity resolveByFlavorName( - String flavorName, List flavors, UUID clientId) { - return flavors.stream() - .filter(f -> flavorName.equals(f.getName())) - .findFirst() - .orElseThrow(() -> { - log.warn("Flavor '{}' is not configured for clientApp '{}'", flavorName, clientId); - return new ProjectValidationException(ErrorKey.INVALID_PROJECT_FLAVOR); - }); - } - - private ClientAppProjectFlavorEntity resolveByConfigurationItem( - String configurationItem, List flavors, UUID clientId) { - List matchingFlavors = flavors.stream() - .filter(f -> configurationItem.equals(f.getConfigItem())) - .toList(); - - if (matchingFlavors.size() != 1) { - log.warn("ConfigItem '{}' does not match exactly one flavor for clientApp '{}'", - configurationItem, clientId); - String message = MessageFormat. - format("Not exists a project flavor configured for the Config Item {0}. " + - "To create a project under {0} the projectFlavor parameter is mandatory.", - configurationItem); - throw new ProjectValidationException(ErrorKey.INVALID_CONFIG_ITEM, message); - } - - ClientAppProjectFlavorEntity matchedFlavor = matchingFlavors.getFirst(); - - if (!isAllowedConfigItem(configurationItem, matchedFlavor)) { - log.warn("ConfigItem '{}' is not in the allowed list for flavor '{}' of clientApp '{}'", - configurationItem, matchedFlavor.getName(), clientId); - throw new ProjectValidationException(ErrorKey.INVALID_CONFIG_ITEM); - } - - return matchedFlavor; - } - - private String resolveProjectKey(String existingProjectKey, ClientAppProjectFlavorEntity flavor) { - try { - if (Strings.isNotEmpty(existingProjectKey)) { - if (!projectExistenceService.isProjectFound(existingProjectKey)) { - return existingProjectKey; - } - - throw new ProjectAlreadyExistsException(ErrorKey.PROJECT_ALREADY_EXISTS); - } - - String pattern = flavor.getProjectKeyPattern(); - - return generateProjectKeyService.generateProjectKey(pattern); - } catch (ProjectKeyGenerationException e) { - throw new ProjectCreationException("Error generating the project key", e); - } catch (ProjectExistenceServiceException e) { - throw new ProjectCreationException("Error checking if the generated key exists: " + e.getMessage(), e); - } - } - - private boolean isAllowedConfigItem(String configurationItem, ClientAppProjectFlavorEntity flavor) { - String[] allowedConfigItems = flavor.getAllowedConfigItems(); - - if (allowedConfigItems == null || allowedConfigItems.length == 0) { - return true; - } - - return Arrays.asList(allowedConfigItems).contains(configurationItem); - } - - private String firstNonBlank(String preferred, String fallback) { - return Strings.isNotEmpty(preferred) ? preferred : fallback; - } - - private String resolveProjectName(String preferred, String fallback) { - if (!Strings.isEmpty(preferred)) { - try { - if (projectExistenceService.isProjectFoundByName(preferred)) { - throw new ProjectAlreadyExistsException(ErrorKey.PROJECT_SAME_PROJECT_NAME_ALREADY_EXISTS); - } - } catch (ProjectExistenceServiceException e) { - throw new ProjectCreationException("Error checking if project name already exists: " + e.getMessage(), e); - } - } - - return firstNonBlank(preferred, fallback); - } -} - +package org.opendevstack.apiservice.project.facade.impl; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.logging.log4j.util.Strings; +import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; +import org.opendevstack.apiservice.persistence.entity.ClientAppProjectFlavorEntity; +import org.opendevstack.apiservice.project.exception.ErrorKey; +import org.opendevstack.apiservice.project.exception.ProjectAlreadyExistsException; +import org.opendevstack.apiservice.project.exception.ProjectCreationException; +import org.opendevstack.apiservice.project.exception.ProjectValidationException; +import org.opendevstack.apiservice.project.model.CreateProjectRequest; +import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; +import org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException; +import org.opendevstack.apiservice.serviceproject.service.GenerateProjectKeyService; +import org.opendevstack.apiservice.serviceproject.service.ProjectExistenceService; +import org.springframework.stereotype.Component; + +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +@Slf4j +public class ProjectCreationCommandBuilder { + + private final GenerateProjectKeyService generateProjectKeyService; + + private final ProjectExistenceService projectExistenceService; + + public ProjectCreationCommand buildForCreation(CreateProjectRequest request, ClientAppEntity clientApp) { + ClientAppProjectFlavorEntity flavor = resolveFlavor(request, clientApp); + String projectKey = resolveProjectKey(request.getProjectKey(), flavor); + + return build(request, flavor, projectKey, clientApp); + } + + public ProjectCreationCommand buildForRegistration(CreateProjectRequest request, ClientAppEntity clientApp) { + ClientAppProjectFlavorEntity flavor = resolveFlavor(request, clientApp); + String projectKey = resolveProjectKeyForRegistration(request.getProjectKey(), flavor); + + return build(request, flavor, projectKey, clientApp); + } + + private ProjectCreationCommand build(CreateProjectRequest request, ClientAppProjectFlavorEntity flavor, String projectKey, ClientAppEntity clientApp) { + String projectFlavor = firstNonBlank(request.getProjectFlavor(), flavor.getName()); + String configurationItem = firstNonBlank(request.getConfigurationItem(), flavor.getConfigItem()); + String owner = firstNonBlank(request.getOwner(), flavor.getProjectOwner()); + String x2account = firstNonBlank(request.getX2OdsAccount(), flavor.getServiceAccount()); + String location = firstNonBlank(request.getLocation(), flavor.getLocation()); + String projectName = resolveProjectName(request.getProjectName(), projectKey); + String projectDescription = firstNonBlank(request.getProjectDescription(), "project " + projectFlavor); + + return new ProjectCreationCommand( + projectKey, + projectName, + projectDescription, + projectFlavor, + configurationItem, + location, + x2account, + owner, + clientApp.getId()); + } + + private ClientAppProjectFlavorEntity resolveFlavor(CreateProjectRequest request, ClientAppEntity clientApp) { + List flavors = clientApp.getProjectFlavors(); + + if (flavors == null || flavors.isEmpty()) { + log.warn("ClientApp '{}' has no project flavors configured", clientApp.getClientId()); + throw new ProjectValidationException(ErrorKey.INVALID_PROJECT_FLAVOR); + } + + if (Strings.isNotEmpty(request.getProjectFlavor())) { + return resolveByFlavorName(request.getProjectFlavor(), flavors, clientApp.getClientId()); + } + + if (Strings.isNotEmpty(request.getConfigurationItem())) { + return resolveByConfigurationItem(request.getConfigurationItem(), flavors, clientApp.getClientId()); + } + + throw new ProjectValidationException(ErrorKey.BAD_REQUEST_FLAVOR_CONFIG_ITEM); + } + + private ClientAppProjectFlavorEntity resolveByFlavorName( + String flavorName, List flavors, UUID clientId) { + return flavors.stream() + .filter(f -> flavorName.equals(f.getName())) + .findFirst() + .orElseThrow(() -> { + log.warn("Flavor '{}' is not configured for clientApp '{}'", flavorName, clientId); + return new ProjectValidationException(ErrorKey.INVALID_PROJECT_FLAVOR); + }); + } + + private ClientAppProjectFlavorEntity resolveByConfigurationItem( + String configurationItem, List flavors, UUID clientId) { + List matchingFlavors = flavors.stream() + .filter(f -> configurationItem.equals(f.getConfigItem())) + .toList(); + + if (matchingFlavors.size() != 1) { + log.warn("ConfigItem '{}' does not match exactly one flavor for clientApp '{}'", + configurationItem, clientId); + String message = MessageFormat. + format("Not exists a project flavor configured for the Config Item {0}. " + + "To create a project under {0} the projectFlavor parameter is mandatory.", + configurationItem); + throw new ProjectValidationException(ErrorKey.INVALID_CONFIG_ITEM, message); + } + + ClientAppProjectFlavorEntity matchedFlavor = matchingFlavors.getFirst(); + + if (!isAllowedConfigItem(configurationItem, matchedFlavor)) { + log.warn("ConfigItem '{}' is not in the allowed list for flavor '{}' of clientApp '{}'", + configurationItem, matchedFlavor.getName(), clientId); + throw new ProjectValidationException(ErrorKey.INVALID_CONFIG_ITEM); + } + + return matchedFlavor; + } + + private String resolveProjectKey(String existingProjectKey, ClientAppProjectFlavorEntity flavor) { + try { + if (Strings.isNotEmpty(existingProjectKey)) { + if (!projectExistenceService.isProjectFound(existingProjectKey)) { + return existingProjectKey; + } + + throw new ProjectAlreadyExistsException(ErrorKey.PROJECT_ALREADY_EXISTS); + } + + String pattern = flavor.getProjectKeyPattern(); + + return generateProjectKeyService.generateProjectKey(pattern); + } catch (ProjectKeyGenerationException e) { + throw new ProjectCreationException("Error generating the project key", e); + } catch (ProjectExistenceServiceException e) { + throw new ProjectCreationException("Error checking if the generated key exists: " + e.getMessage(), e); + } + } + + private String resolveProjectKeyForRegistration(String existingProjectKey, ClientAppProjectFlavorEntity flavor) { + try { + if (Strings.isNotEmpty(existingProjectKey)) { + if (!projectExistenceService.isProjectFoundInCollection(existingProjectKey)) { + return existingProjectKey; + } + + throw new ProjectAlreadyExistsException(ErrorKey.PROJECT_ALREADY_EXISTS); + } + + String pattern = flavor.getProjectKeyPattern(); + + return generateProjectKeyService.generateProjectKey(pattern); + } catch (ProjectKeyGenerationException e) { + throw new ProjectCreationException("Error generating the project key", e); + } catch (ProjectExistenceServiceException e) { + throw new ProjectCreationException("Error checking if the generated key exists: " + e.getMessage(), e); + } + } + + private boolean isAllowedConfigItem(String configurationItem, ClientAppProjectFlavorEntity flavor) { + String[] allowedConfigItems = flavor.getAllowedConfigItems(); + + if (allowedConfigItems == null || allowedConfigItems.length == 0) { + return true; + } + + return Arrays.asList(allowedConfigItems).contains(configurationItem); + } + + private String firstNonBlank(String preferred, String fallback) { + return Strings.isNotEmpty(preferred) ? preferred : fallback; + } + + private String resolveProjectName(String preferred, String fallback) { + if (!Strings.isEmpty(preferred)) { + try { + if (projectExistenceService.isProjectFoundByName(preferred)) { + throw new ProjectAlreadyExistsException(ErrorKey.PROJECT_SAME_PROJECT_NAME_ALREADY_EXISTS); + } + } catch (ProjectExistenceServiceException e) { + throw new ProjectCreationException("Error checking if project name already exists: " + e.getMessage(), e); + } + } + + return firstNonBlank(preferred, fallback); + } +} + diff --git a/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImpl.java b/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImpl.java index 746fcd4..88a6e20 100644 --- a/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImpl.java +++ b/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImpl.java @@ -1,123 +1,124 @@ -package org.opendevstack.apiservice.project.facade.impl; - -import lombok.extern.slf4j.Slf4j; -import org.opendevstack.apiservice.externalservice.aap.model.AutomationExecutionResult; -import org.opendevstack.apiservice.externalservice.aap.service.AutomationPlatformService; -import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; -import org.opendevstack.apiservice.project.exception.ProjectCreationException; -import org.opendevstack.apiservice.project.facade.ProjectsFacade; -import org.opendevstack.apiservice.project.mapper.AutomationParametersMapper; -import org.opendevstack.apiservice.project.mapper.ProjectCreationResponseMapper; -import org.opendevstack.apiservice.project.mapper.ProjectMapper; -import org.opendevstack.apiservice.project.model.CreateProjectRequest; -import org.opendevstack.apiservice.project.model.CreateProjectResponse; -import org.opendevstack.apiservice.project.service.ClientAppService; -import org.opendevstack.apiservice.serviceproject.model.ProjectRequest; -import org.opendevstack.apiservice.serviceproject.model.ProjectResponse; -import org.opendevstack.apiservice.serviceproject.model.Status; -import org.opendevstack.apiservice.serviceproject.service.ProjectService; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import java.util.Map; -import java.util.UUID; - -@Component("apiProjectFacadeImpl") -@Slf4j -public class ProjectsFacadeImpl implements ProjectsFacade { - - @Value("${apis.projects.ansible-workflow-name}") - private String createProjectWorkflow; - - private final ProjectService projectService; - - private final ProjectMapper projectMapper; - - private final AutomationParametersMapper automationParametersMapper; - - private final ProjectCreationResponseMapper projectCreationResponseMapper; - - private final ProjectCreationCommandBuilder projectCreationCommandBuilder; - - private final ClientAppService clientAppService; - - private final AutomationPlatformService automationPlatformService; - - public ProjectsFacadeImpl( - ProjectService projectService, - ProjectMapper projectMapper, - AutomationParametersMapper automationParametersMapper, - ProjectCreationResponseMapper projectCreationResponseMapper, - ProjectCreationCommandBuilder projectCreationCommandBuilder, - ClientAppService clientAppService, - AutomationPlatformService automationPlatformService) { - this.projectService = projectService; - this.projectMapper = projectMapper; - this.automationParametersMapper = automationParametersMapper; - this.projectCreationResponseMapper = projectCreationResponseMapper; - this.projectCreationCommandBuilder = projectCreationCommandBuilder; - this.clientAppService = clientAppService; - this.automationPlatformService = automationPlatformService; - } - - @Override - public CreateProjectResponse createProject(CreateProjectRequest request, UUID clientId) { - - ClientAppEntity clientApp = clientAppService.findByClientId(clientId); - - ProjectCreationCommand command = projectCreationCommandBuilder.build(request, clientApp); - - ProjectResponse project; - - if (Boolean.TRUE.equals(request.getRegistrationOnly())) { - project = registerProject(command); - } else { - project = createNewProject(command); - } - - return projectCreationResponseMapper.toSuccessResponse(command, project); - } - - @Override - public CreateProjectResponse getProject(String projectKey) { - return projectMapper.toApiResponse(projectService.getProject(projectKey)); - } - - private ProjectResponse registerProject(ProjectCreationCommand command) { - - ProjectRequest projectRequest = projectMapper.toServiceRequest(command); - projectRequest.setStatus(Status.RUNNING); - projectRequest.setProjectFlavor("REGULAR"); - - return projectService.saveProject(projectRequest); - } - - private ProjectResponse createNewProject(ProjectCreationCommand command) { - - ProjectRequest projectRequest = projectMapper.toServiceRequest(command); - projectRequest.setStatus(Status.PENDING); - - ProjectResponse project = projectService.saveProject(projectRequest); - - initializeProject(command, projectRequest, project); - - return project; - } - - private void initializeProject( - ProjectCreationCommand command, ProjectRequest projectRequest, ProjectResponse project) { - String projectId = project.getProjectId().toString(); - Map workflowParameters = automationParametersMapper.toWorkflowParameters(command, projectId); - - AutomationExecutionResult automationExecutionResult = automationPlatformService - .executeWorkflow(createProjectWorkflow, workflowParameters); - - if (!automationExecutionResult.isSuccessful()) { - projectRequest.setProjectId(project.getProjectId()); - projectRequest.setStatus(Status.FAILED); - projectService.saveProject(projectRequest); - throw new ProjectCreationException("Failed to create project: " - + automationExecutionResult.getErrorDetails()); - } - } +package org.opendevstack.apiservice.project.facade.impl; + +import lombok.extern.slf4j.Slf4j; +import org.opendevstack.apiservice.externalservice.aap.model.AutomationExecutionResult; +import org.opendevstack.apiservice.externalservice.aap.service.AutomationPlatformService; +import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; +import org.opendevstack.apiservice.project.exception.ProjectCreationException; +import org.opendevstack.apiservice.project.facade.ProjectsFacade; +import org.opendevstack.apiservice.project.mapper.AutomationParametersMapper; +import org.opendevstack.apiservice.project.mapper.ProjectCreationResponseMapper; +import org.opendevstack.apiservice.project.mapper.ProjectMapper; +import org.opendevstack.apiservice.project.model.CreateProjectRequest; +import org.opendevstack.apiservice.project.model.CreateProjectResponse; +import org.opendevstack.apiservice.project.service.ClientAppService; +import org.opendevstack.apiservice.serviceproject.model.ProjectRequest; +import org.opendevstack.apiservice.serviceproject.model.ProjectResponse; +import org.opendevstack.apiservice.serviceproject.model.Status; +import org.opendevstack.apiservice.serviceproject.service.ProjectService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.UUID; + +@Component("apiProjectFacadeImpl") +@Slf4j +public class ProjectsFacadeImpl implements ProjectsFacade { + + @Value("${apis.projects.ansible-workflow-name}") + private String createProjectWorkflow; + + private final ProjectService projectService; + + private final ProjectMapper projectMapper; + + private final AutomationParametersMapper automationParametersMapper; + + private final ProjectCreationResponseMapper projectCreationResponseMapper; + + private final ProjectCreationCommandBuilder projectCreationCommandBuilder; + + private final ClientAppService clientAppService; + + private final AutomationPlatformService automationPlatformService; + + public ProjectsFacadeImpl( + ProjectService projectService, + ProjectMapper projectMapper, + AutomationParametersMapper automationParametersMapper, + ProjectCreationResponseMapper projectCreationResponseMapper, + ProjectCreationCommandBuilder projectCreationCommandBuilder, + ClientAppService clientAppService, + AutomationPlatformService automationPlatformService) { + this.projectService = projectService; + this.projectMapper = projectMapper; + this.automationParametersMapper = automationParametersMapper; + this.projectCreationResponseMapper = projectCreationResponseMapper; + this.projectCreationCommandBuilder = projectCreationCommandBuilder; + this.clientAppService = clientAppService; + this.automationPlatformService = automationPlatformService; + } + + @Override + public CreateProjectResponse createProject(CreateProjectRequest request, UUID clientId) { + + ClientAppEntity clientApp = clientAppService.findByClientId(clientId); + + ProjectCreationCommand command; + ProjectResponse project; + + if (Boolean.TRUE.equals(request.getRegistrationOnly())) { + command = projectCreationCommandBuilder.buildForRegistration(request, clientApp); + project = registerProject(command); + } else { + command = projectCreationCommandBuilder.buildForCreation(request, clientApp); + project = createNewProject(command); + } + + return projectCreationResponseMapper.toSuccessResponse(command, project); + } + + @Override + public CreateProjectResponse getProject(String projectKey) { + return projectMapper.toApiResponse(projectService.getProject(projectKey)); + } + + private ProjectResponse registerProject(ProjectCreationCommand command) { + + ProjectRequest projectRequest = projectMapper.toServiceRequest(command); + projectRequest.setStatus(Status.RUNNING); + projectRequest.setProjectFlavor("REGULAR"); + + return projectService.saveProject(projectRequest); + } + + private ProjectResponse createNewProject(ProjectCreationCommand command) { + + ProjectRequest projectRequest = projectMapper.toServiceRequest(command); + projectRequest.setStatus(Status.PENDING); + + ProjectResponse project = projectService.saveProject(projectRequest); + + initializeProject(command, projectRequest, project); + + return project; + } + + private void initializeProject( + ProjectCreationCommand command, ProjectRequest projectRequest, ProjectResponse project) { + String projectId = project.getProjectId().toString(); + Map workflowParameters = automationParametersMapper.toWorkflowParameters(command, projectId); + + AutomationExecutionResult automationExecutionResult = automationPlatformService + .executeWorkflow(createProjectWorkflow, workflowParameters); + + if (!automationExecutionResult.isSuccessful()) { + projectRequest.setProjectId(project.getProjectId()); + projectRequest.setStatus(Status.FAILED); + projectService.saveProject(projectRequest); + throw new ProjectCreationException("Failed to create project: " + + automationExecutionResult.getErrorDetails()); + } + } } \ No newline at end of file diff --git a/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilderTest.java b/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilderTest.java index 0c71c77..377a4fa 100644 --- a/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilderTest.java +++ b/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilderTest.java @@ -1,208 +1,240 @@ -package org.opendevstack.apiservice.project.facade.impl; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; -import org.opendevstack.apiservice.persistence.entity.ClientAppProjectFlavorEntity; -import org.opendevstack.apiservice.project.exception.ErrorKey; -import org.opendevstack.apiservice.project.exception.ProjectAlreadyExistsException; -import org.opendevstack.apiservice.project.exception.ProjectCreationException; -import org.opendevstack.apiservice.project.exception.ProjectValidationException; -import org.opendevstack.apiservice.project.model.CreateProjectRequest; -import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; -import org.opendevstack.apiservice.serviceproject.service.GenerateProjectKeyService; -import org.opendevstack.apiservice.serviceproject.service.ProjectExistenceService; - -import java.util.List; -import java.util.UUID; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -class ProjectCreationCommandBuilderTest { - - private static final UUID CLIENT_ID = UUID.fromString("00000000-0000-0000-0000-000000000001"); - - @Mock - private GenerateProjectKeyService generateProjectKeyService; - - @Mock - private ProjectExistenceService projectExistenceService; - - private ProjectCreationCommandBuilder sut; - - private AutoCloseable mocks; - - @BeforeEach - void set_up() { - mocks = MockitoAnnotations.openMocks(this); - sut = new ProjectCreationCommandBuilder(generateProjectKeyService, projectExistenceService); - } - - @AfterEach - void tear_down() throws Exception { - mocks.close(); - } - - @Test - void build_resolves_defaults_from_flavor_when_request_fields_are_missing() throws ProjectExistenceServiceException { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request("DLSS", null, "KEY01"); - request.setOwner(null); - request.setLocation(null); - - when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); - - ProjectCreationCommand result = sut.build(request, clientApp); - - assertEquals("DLSS", result.getProjectFlavor()); - assertEquals("CI-001", result.getConfigurationItem()); - assertEquals("owner1", result.getOwner()); - assertEquals("eu", result.getLocation()); - assertEquals("KEY01", result.getProjectKey()); - } - - @Test - void build_resolves_flavor_from_configuration_item_when_flavor_is_not_provided() throws ProjectExistenceServiceException { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request(null, "CI-001", "KEY01"); - - when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); - - ProjectCreationCommand result = sut.build(request, clientApp); - - assertEquals("DLSS", result.getProjectFlavor()); - assertEquals("CI-001", result.getConfigurationItem()); - } - - @Test - void build_generates_project_key_when_request_project_key_is_null() throws ProjectExistenceServiceException, org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request("DLSS", null, null); - - when(generateProjectKeyService.generateProjectKey("DLSS%06d")).thenReturn("DLSS000001"); - when(projectExistenceService.isProjectFound("DLSS000001")).thenReturn(false); - - ProjectCreationCommand result = sut.build(request, clientApp); - - assertEquals("DLSS000001", result.getProjectKey()); - verify(generateProjectKeyService).generateProjectKey("DLSS%06d"); - } - - @Test - void build_throws_project_already_exists_exception_when_project_key_already_exists() throws ProjectExistenceServiceException { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request("DLSS", null, "KEY01"); - - when(projectExistenceService.isProjectFound("KEY01")).thenReturn(true); - - ProjectAlreadyExistsException ex = assertThrows(ProjectAlreadyExistsException.class, - () -> sut.build(request, clientApp)); - assertEquals(ErrorKey.PROJECT_ALREADY_EXISTS, ex.getErrorKey()); - } - - @Test - void build_throws_validation_exception_when_flavor_and_config_item_are_missing() { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request(null, null, "KEY01"); - - ProjectValidationException ex = assertThrows(ProjectValidationException.class, - () -> sut.build(request, clientApp)); - assertEquals(ErrorKey.BAD_REQUEST_FLAVOR_CONFIG_ITEM, ex.getErrorKey()); - } - - @Test - void build_throws_validation_exception_when_configuration_item_matches_multiple_flavors() { - ClientAppEntity clientApp = build_client_app(List.of( - build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"), - build_flavor("AMP", "CI-001", new String[] {}, "eu", "owner2"))); - CreateProjectRequest request = build_request(null, "CI-001", "KEY01"); - - ProjectValidationException ex = assertThrows(ProjectValidationException.class, - () -> sut.build(request, clientApp)); - assertEquals(ErrorKey.INVALID_CONFIG_ITEM, ex.getErrorKey()); - } - - @Test - void build_throws_project_key_generation_exception_when_generation_fails() throws Exception { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request("DLSS", null, null); - - when(generateProjectKeyService.generateProjectKey("DLSS%06d")) - .thenThrow(new org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException("fail")); - - assertThrows(ProjectCreationException.class, () -> sut.build(request, clientApp)); - } - - @Test - void build_throws_project_already_exists_exception_when_project_name_already_exists() throws Exception { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request("DLSS", null, "KEY01"); - request.setProjectName("Existing Project"); - - when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); - when(projectExistenceService.isProjectFoundByName("Existing Project")).thenReturn(true); - - assertThrows(ProjectAlreadyExistsException.class, () -> sut.build(request, clientApp)); - } - - @Test - void build_throws_project_creation_exception_when_project_name_check_fails() throws Exception { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request("DLSS", null, "KEY01"); - request.setProjectName("Any Project"); - - when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); - when(projectExistenceService.isProjectFoundByName("Any Project")) - .thenThrow(new ProjectExistenceServiceException("lookup failed")); - - assertThrows(ProjectCreationException.class, () -> sut.build(request, clientApp)); - } - - private CreateProjectRequest build_request(String flavor, String configItem, String projectKey) { - CreateProjectRequest request = new CreateProjectRequest(); - request.setProjectKey(projectKey); - request.setProjectFlavor(flavor); - request.setConfigurationItem(configItem); - request.setProjectName("Test Project"); - request.setProjectDescription("A test project"); - request.setOwner("testowner"); - request.setLocation("eu"); - request.setX2OdsAccount("x2test"); - return request; - } - - private ClientAppEntity build_client_app(List flavors) { - ClientAppEntity entity = ClientAppEntity.builder() - .clientId(CLIENT_ID) - .clientName("Test App") - .build(); - entity.setProjectFlavors(flavors); - return entity; - } - - private ClientAppProjectFlavorEntity build_flavor( - String name, String configItem, String[] allowedConfigItems, String location, String projectOwner) { - return ClientAppProjectFlavorEntity.builder() - .name(name) - .configItem(configItem) - .allowedConfigItems(allowedConfigItems) - .projectKeyPattern(name + "%06d") - .location(location) - .projectOwner(projectOwner) - .build(); - } -} +package org.opendevstack.apiservice.project.facade.impl; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; +import org.opendevstack.apiservice.persistence.entity.ClientAppProjectFlavorEntity; +import org.opendevstack.apiservice.project.exception.ErrorKey; +import org.opendevstack.apiservice.project.exception.ProjectAlreadyExistsException; +import org.opendevstack.apiservice.project.exception.ProjectCreationException; +import org.opendevstack.apiservice.project.exception.ProjectValidationException; +import org.opendevstack.apiservice.project.model.CreateProjectRequest; +import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; +import org.opendevstack.apiservice.serviceproject.service.GenerateProjectKeyService; +import org.opendevstack.apiservice.serviceproject.service.ProjectExistenceService; + +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class ProjectCreationCommandBuilderTest { + + private static final UUID CLIENT_ID = UUID.fromString("00000000-0000-0000-0000-000000000001"); + + @Mock + private GenerateProjectKeyService generateProjectKeyService; + + @Mock + private ProjectExistenceService projectExistenceService; + + private ProjectCreationCommandBuilder sut; + + private AutoCloseable mocks; + + @BeforeEach + void set_up() { + mocks = MockitoAnnotations.openMocks(this); + sut = new ProjectCreationCommandBuilder(generateProjectKeyService, projectExistenceService); + } + + @AfterEach + void tear_down() throws Exception { + mocks.close(); + } + + @Test + void build_for_creation_resolves_defaults_from_flavor_when_request_fields_are_missing() throws ProjectExistenceServiceException { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, "KEY01"); + request.setOwner(null); + request.setLocation(null); + + when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); + + ProjectCreationCommand result = sut.buildForCreation(request, clientApp); + + assertEquals("DLSS", result.getProjectFlavor()); + assertEquals("CI-001", result.getConfigurationItem()); + assertEquals("owner1", result.getOwner()); + assertEquals("eu", result.getLocation()); + assertEquals("KEY01", result.getProjectKey()); + } + + @Test + void build_for_creation_resolves_flavor_from_configuration_item_when_flavor_is_not_provided() throws ProjectExistenceServiceException { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request(null, "CI-001", "KEY01"); + + when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); + + ProjectCreationCommand result = sut.buildForCreation(request, clientApp); + + assertEquals("DLSS", result.getProjectFlavor()); + assertEquals("CI-001", result.getConfigurationItem()); + } + + @Test + void build_for_creation_generates_project_key_when_request_project_key_is_null() throws ProjectExistenceServiceException, org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, null); + + when(generateProjectKeyService.generateProjectKey("DLSS%06d")).thenReturn("DLSS000001"); + when(projectExistenceService.isProjectFound("DLSS000001")).thenReturn(false); + + ProjectCreationCommand result = sut.buildForCreation(request, clientApp); + + assertEquals("DLSS000001", result.getProjectKey()); + verify(generateProjectKeyService).generateProjectKey("DLSS%06d"); + } + + @Test + void build_for_creation_throws_project_already_exists_exception_when_project_key_already_exists() throws ProjectExistenceServiceException { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, "KEY01"); + + when(projectExistenceService.isProjectFound("KEY01")).thenReturn(true); + + ProjectAlreadyExistsException ex = assertThrows(ProjectAlreadyExistsException.class, + () -> sut.buildForCreation(request, clientApp)); + assertEquals(ErrorKey.PROJECT_ALREADY_EXISTS, ex.getErrorKey()); + } + + @Test + void build_for_creation_throws_validation_exception_when_flavor_and_config_item_are_missing() { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request(null, null, "KEY01"); + + ProjectValidationException ex = assertThrows(ProjectValidationException.class, + () -> sut.buildForCreation(request, clientApp)); + assertEquals(ErrorKey.BAD_REQUEST_FLAVOR_CONFIG_ITEM, ex.getErrorKey()); + } + + @Test + void build_for_creation_throws_validation_exception_when_configuration_item_matches_multiple_flavors() { + ClientAppEntity clientApp = build_client_app(List.of( + build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"), + build_flavor("AMP", "CI-001", new String[] {}, "eu", "owner2"))); + CreateProjectRequest request = build_request(null, "CI-001", "KEY01"); + + ProjectValidationException ex = assertThrows(ProjectValidationException.class, + () -> sut.buildForCreation(request, clientApp)); + assertEquals(ErrorKey.INVALID_CONFIG_ITEM, ex.getErrorKey()); + } + + @Test + void build_for_creation_throws_project_key_generation_exception_when_generation_fails() throws Exception { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, null); + + when(generateProjectKeyService.generateProjectKey("DLSS%06d")) + .thenThrow(new org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException("fail")); + + assertThrows(ProjectCreationException.class, () -> sut.buildForCreation(request, clientApp)); + } + + @Test + void build_for_creation_throws_project_already_exists_exception_when_project_name_already_exists() throws Exception { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, "KEY01"); + request.setProjectName("Existing Project"); + + when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); + when(projectExistenceService.isProjectFoundByName("Existing Project")).thenReturn(true); + + assertThrows(ProjectAlreadyExistsException.class, () -> sut.buildForCreation(request, clientApp)); + } + + @Test + void build_for_creation_throws_project_creation_exception_when_project_name_check_fails() throws Exception { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, "KEY01"); + request.setProjectName("Any Project"); + + when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); + when(projectExistenceService.isProjectFoundByName("Any Project")) + .thenThrow(new ProjectExistenceServiceException("lookup failed")); + + assertThrows(ProjectCreationException.class, () -> sut.buildForCreation(request, clientApp)); + } + + @Test + void build_for_registration_resolves_defaults_from_flavor_when_request_fields_are_missing() throws ProjectExistenceServiceException { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, "KEY01"); + request.setOwner(null); + request.setLocation(null); + + when(projectExistenceService.isProjectFoundInCollection("KEY01")).thenReturn(false); + + ProjectCreationCommand result = sut.buildForRegistration(request, clientApp); + + assertEquals("DLSS", result.getProjectFlavor()); + assertEquals("CI-001", result.getConfigurationItem()); + assertEquals("owner1", result.getOwner()); + assertEquals("eu", result.getLocation()); + assertEquals("KEY01", result.getProjectKey()); + } + + @Test + void build_for_registration_throws_project_already_exists_exception_when_project_key_already_exists() throws ProjectExistenceServiceException { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, "KEY01"); + + when(projectExistenceService.isProjectFoundInCollection("KEY01")).thenReturn(true); + + ProjectAlreadyExistsException ex = assertThrows(ProjectAlreadyExistsException.class, + () -> sut.buildForRegistration(request, clientApp)); + assertEquals(ErrorKey.PROJECT_ALREADY_EXISTS, ex.getErrorKey()); + } + + private CreateProjectRequest build_request(String flavor, String configItem, String projectKey) { + CreateProjectRequest request = new CreateProjectRequest(); + request.setProjectKey(projectKey); + request.setProjectFlavor(flavor); + request.setConfigurationItem(configItem); + request.setProjectName("Test Project"); + request.setProjectDescription("A test project"); + request.setOwner("testowner"); + request.setLocation("eu"); + request.setX2OdsAccount("x2test"); + return request; + } + + private ClientAppEntity build_client_app(List flavors) { + ClientAppEntity entity = ClientAppEntity.builder() + .clientId(CLIENT_ID) + .clientName("Test App") + .build(); + entity.setProjectFlavors(flavors); + return entity; + } + + private ClientAppProjectFlavorEntity build_flavor( + String name, String configItem, String[] allowedConfigItems, String location, String projectOwner) { + return ClientAppProjectFlavorEntity.builder() + .name(name) + .configItem(configItem) + .allowedConfigItems(allowedConfigItems) + .projectKeyPattern(name + "%06d") + .location(location) + .projectOwner(projectOwner) + .build(); + } +} diff --git a/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImplTest.java b/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImplTest.java index dd622ef..595657a 100644 --- a/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImplTest.java +++ b/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImplTest.java @@ -1,211 +1,211 @@ -package org.opendevstack.apiservice.project.facade.impl; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendevstack.apiservice.externalservice.aap.model.AutomationExecutionResult; -import org.opendevstack.apiservice.externalservice.aap.service.AutomationPlatformService; -import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; -import org.opendevstack.apiservice.project.exception.ProjectCreationException; -import org.opendevstack.apiservice.project.mapper.AutomationParametersMapper; -import org.opendevstack.apiservice.project.mapper.ProjectCreationResponseMapper; -import org.opendevstack.apiservice.project.mapper.ProjectMapper; -import org.opendevstack.apiservice.project.model.CreateProjectRequest; -import org.opendevstack.apiservice.project.model.CreateProjectResponse; -import org.opendevstack.apiservice.project.service.ClientAppService; -import org.opendevstack.apiservice.serviceproject.model.ProjectRequest; -import org.opendevstack.apiservice.serviceproject.model.ProjectResponse; -import org.opendevstack.apiservice.serviceproject.model.Status; -import org.opendevstack.apiservice.serviceproject.service.ProjectService; - -import java.lang.reflect.Field; -import java.util.Map; -import java.util.UUID; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyMap; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -class ProjectsFacadeImplTest { - - private static final UUID CLIENT_ID = UUID.fromString("00000000-0000-0000-0000-000000000001"); - - @Mock - private ProjectService projectService; - - @Mock - private ProjectMapper projectMapper; - - @Mock - private AutomationParametersMapper automationParametersMapper; - - @Mock - private ProjectCreationResponseMapper projectCreationResponseMapper; - - @Mock - private ProjectCreationCommandBuilder projectCreationCommandBuilder; - - @Mock - private ClientAppService clientAppService; - - @Mock - private AutomationPlatformService automationPlatformService; - - private ProjectsFacadeImpl sut; - private AutoCloseable mocks; - - @BeforeEach - void set_up() throws Exception { - mocks = MockitoAnnotations.openMocks(this); - sut = new ProjectsFacadeImpl( - projectService, - projectMapper, - automationParametersMapper, - projectCreationResponseMapper, - projectCreationCommandBuilder, - clientAppService, - automationPlatformService); - - Field workflowField = ProjectsFacadeImpl.class.getDeclaredField("createProjectWorkflow"); - workflowField.setAccessible(true); - workflowField.set(sut, "create-project-workflow"); - } - - @AfterEach - void tear_down() throws Exception { - mocks.close(); - } - - @Test - void create_project_returns_success_response_when_automation_is_successful() { - CreateProjectRequest request = new CreateProjectRequest(); - ClientAppEntity clientApp = ClientAppEntity.builder().clientId(CLIENT_ID).build(); - ProjectCreationCommand command = new ProjectCreationCommand( - "DLSS01", "name", "desc", "DLSS", "CI-001", "eu", "x2test", "owner", CLIENT_ID); - ProjectRequest serviceRequest = new ProjectRequest(); - ProjectResponse projectResponse = ProjectResponse.builder() - .projectId(UUID.fromString("11111111-1111-1111-1111-111111111111")) - .projectKey("DLSS01") - .status(Status.PENDING) - .build(); - CreateProjectResponse apiResponse = new CreateProjectResponse(); - apiResponse.setStatus("Pending"); - apiResponse.setProjectFlavor("DLSS"); - - when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); - when(projectCreationCommandBuilder.build(request, clientApp)).thenReturn(command); - when(projectMapper.toServiceRequest(command)).thenReturn(serviceRequest); - when(projectService.saveProject(serviceRequest)).thenReturn(projectResponse); - when(automationParametersMapper.toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111")) - .thenReturn(Map.of("project_key", "DLSS01")); - when(automationPlatformService.executeWorkflow(anyString(), anyMap())) - .thenReturn(AutomationExecutionResult.success("job-1", "ok")); - when(projectCreationResponseMapper.toSuccessResponse(command, projectResponse)).thenReturn(apiResponse); - - CreateProjectResponse result = sut.createProject(request, CLIENT_ID); - - assertEquals("Pending", result.getStatus()); - assertEquals("DLSS", result.getProjectFlavor()); - verify(projectCreationCommandBuilder).build(request, clientApp); - verify(projectMapper).toServiceRequest(command); - verify(automationParametersMapper) - .toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111"); - verify(projectCreationResponseMapper).toSuccessResponse(command, projectResponse); - } - - @Test - void create_project_with_registrationOnly_returns_success_response_when_automation_is_not_executed() { - CreateProjectRequest request = new CreateProjectRequest(); - request.setRegistrationOnly(true); - ClientAppEntity clientApp = ClientAppEntity.builder().clientId(CLIENT_ID).build(); - ProjectCreationCommand command = new ProjectCreationCommand( - "P3RO01", "name", "desc", null, "CI-001", "eu", "x2test", "owner", CLIENT_ID); - ProjectRequest serviceRequest = new ProjectRequest(); - ProjectResponse projectResponse = ProjectResponse.builder() - .projectId(UUID.fromString("11111111-1111-1111-1111-111111111111")) - .projectKey("P3RO01") - .status(Status.RUNNING) - .build(); - CreateProjectResponse apiResponse = new CreateProjectResponse(); - apiResponse.setStatus("Running"); - apiResponse.setProjectFlavor("REGULAR"); - - when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); - when(projectCreationCommandBuilder.build(request, clientApp)).thenReturn(command); - when(projectMapper.toServiceRequest(command)).thenReturn(serviceRequest); - when(projectService.saveProject(serviceRequest)).thenReturn(projectResponse); - when(projectCreationResponseMapper.toSuccessResponse(command, projectResponse)).thenReturn(apiResponse); - - CreateProjectResponse result = sut.createProject(request, CLIENT_ID); - - assertEquals("Running", result.getStatus()); - assertEquals("REGULAR", result.getProjectFlavor()); - verify(projectCreationCommandBuilder).build(request, clientApp); - verify(projectMapper).toServiceRequest(command); - verify(automationParametersMapper, never()) - .toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111"); - verify(automationPlatformService, never()).executeWorkflow(anyString(), anyMap()); - verify(projectCreationResponseMapper).toSuccessResponse(command, projectResponse); - } - - @Test - void create_project_throws_project_creation_exception_when_automation_is_not_successful() { - CreateProjectRequest request = new CreateProjectRequest(); - ClientAppEntity clientApp = ClientAppEntity.builder().clientId(CLIENT_ID).build(); - ProjectCreationCommand command = new ProjectCreationCommand( - "DLSS01", "name", "desc", "DLSS", "CI-001", "eu", "x2test", "owner", CLIENT_ID); - - when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); - when(projectCreationCommandBuilder.build(request, clientApp)).thenReturn(command); - when(projectMapper.toServiceRequest(command)).thenReturn(new ProjectRequest()); - when(projectService.saveProject(any(ProjectRequest.class))) - .thenReturn(ProjectResponse.builder() - .projectId(UUID.fromString("11111111-1111-1111-1111-111111111111")) - .projectKey("DLSS01") - .status(Status.PENDING) - .build()); - when(automationParametersMapper.toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111")) - .thenReturn(Map.of()); - when(automationPlatformService.executeWorkflow(anyString(), anyMap())) - .thenReturn(AutomationExecutionResult.failure("job-1", "error", "workflow failed")); - - assertThrows(ProjectCreationException.class, () -> sut.createProject(request, CLIENT_ID)); - } - - @Test - void get_project_returns_mapped_response_when_service_returns_project() { - ProjectResponse serviceResponse = ProjectResponse.builder() - .projectKey("PROJ01") - .status(Status.RUNNING) - .build(); - CreateProjectResponse mappedResponse = new CreateProjectResponse(); - mappedResponse.setProjectKey("PROJ01"); - mappedResponse.setStatus("Running"); - - when(projectService.getProject("PROJ01")).thenReturn(serviceResponse); - when(projectMapper.toApiResponse(serviceResponse)).thenReturn(mappedResponse); - - CreateProjectResponse result = sut.getProject("PROJ01"); - - assertEquals("PROJ01", result.getProjectKey()); - assertEquals("Running", result.getStatus()); - } - - @Test - void get_project_returns_null_when_service_returns_null() { - when(projectService.getProject("UNKNOWN")).thenReturn(null); - when(projectMapper.toApiResponse(null)).thenReturn(null); - - CreateProjectResponse result = sut.getProject("UNKNOWN"); - - assertNull(result); - } -} +package org.opendevstack.apiservice.project.facade.impl; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendevstack.apiservice.externalservice.aap.model.AutomationExecutionResult; +import org.opendevstack.apiservice.externalservice.aap.service.AutomationPlatformService; +import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; +import org.opendevstack.apiservice.project.exception.ProjectCreationException; +import org.opendevstack.apiservice.project.mapper.AutomationParametersMapper; +import org.opendevstack.apiservice.project.mapper.ProjectCreationResponseMapper; +import org.opendevstack.apiservice.project.mapper.ProjectMapper; +import org.opendevstack.apiservice.project.model.CreateProjectRequest; +import org.opendevstack.apiservice.project.model.CreateProjectResponse; +import org.opendevstack.apiservice.project.service.ClientAppService; +import org.opendevstack.apiservice.serviceproject.model.ProjectRequest; +import org.opendevstack.apiservice.serviceproject.model.ProjectResponse; +import org.opendevstack.apiservice.serviceproject.model.Status; +import org.opendevstack.apiservice.serviceproject.service.ProjectService; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class ProjectsFacadeImplTest { + + private static final UUID CLIENT_ID = UUID.fromString("00000000-0000-0000-0000-000000000001"); + + @Mock + private ProjectService projectService; + + @Mock + private ProjectMapper projectMapper; + + @Mock + private AutomationParametersMapper automationParametersMapper; + + @Mock + private ProjectCreationResponseMapper projectCreationResponseMapper; + + @Mock + private ProjectCreationCommandBuilder projectCreationCommandBuilder; + + @Mock + private ClientAppService clientAppService; + + @Mock + private AutomationPlatformService automationPlatformService; + + private ProjectsFacadeImpl sut; + private AutoCloseable mocks; + + @BeforeEach + void set_up() throws Exception { + mocks = MockitoAnnotations.openMocks(this); + sut = new ProjectsFacadeImpl( + projectService, + projectMapper, + automationParametersMapper, + projectCreationResponseMapper, + projectCreationCommandBuilder, + clientAppService, + automationPlatformService); + + Field workflowField = ProjectsFacadeImpl.class.getDeclaredField("createProjectWorkflow"); + workflowField.setAccessible(true); + workflowField.set(sut, "create-project-workflow"); + } + + @AfterEach + void tear_down() throws Exception { + mocks.close(); + } + + @Test + void create_project_returns_success_response_when_automation_is_successful() { + CreateProjectRequest request = new CreateProjectRequest(); + ClientAppEntity clientApp = ClientAppEntity.builder().clientId(CLIENT_ID).build(); + ProjectCreationCommand command = new ProjectCreationCommand( + "DLSS01", "name", "desc", "DLSS", "CI-001", "eu", "x2test", "owner", CLIENT_ID); + ProjectRequest serviceRequest = new ProjectRequest(); + ProjectResponse projectResponse = ProjectResponse.builder() + .projectId(UUID.fromString("11111111-1111-1111-1111-111111111111")) + .projectKey("DLSS01") + .status(Status.PENDING) + .build(); + CreateProjectResponse apiResponse = new CreateProjectResponse(); + apiResponse.setStatus("Pending"); + apiResponse.setProjectFlavor("DLSS"); + + when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); + when(projectCreationCommandBuilder.buildForCreation(request, clientApp)).thenReturn(command); + when(projectMapper.toServiceRequest(command)).thenReturn(serviceRequest); + when(projectService.saveProject(serviceRequest)).thenReturn(projectResponse); + when(automationParametersMapper.toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111")) + .thenReturn(Map.of("project_key", "DLSS01")); + when(automationPlatformService.executeWorkflow(anyString(), anyMap())) + .thenReturn(AutomationExecutionResult.success("job-1", "ok")); + when(projectCreationResponseMapper.toSuccessResponse(command, projectResponse)).thenReturn(apiResponse); + + CreateProjectResponse result = sut.createProject(request, CLIENT_ID); + + assertEquals("Pending", result.getStatus()); + assertEquals("DLSS", result.getProjectFlavor()); + verify(projectCreationCommandBuilder).buildForCreation(request, clientApp); + verify(projectMapper).toServiceRequest(command); + verify(automationParametersMapper) + .toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111"); + verify(projectCreationResponseMapper).toSuccessResponse(command, projectResponse); + } + + @Test + void create_project_with_registrationOnly_returns_success_response_when_automation_is_not_executed() { + CreateProjectRequest request = new CreateProjectRequest(); + request.setRegistrationOnly(true); + ClientAppEntity clientApp = ClientAppEntity.builder().clientId(CLIENT_ID).build(); + ProjectCreationCommand command = new ProjectCreationCommand( + "P3RO01", "name", "desc", null, "CI-001", "eu", "x2test", "owner", CLIENT_ID); + ProjectRequest serviceRequest = new ProjectRequest(); + ProjectResponse projectResponse = ProjectResponse.builder() + .projectId(UUID.fromString("11111111-1111-1111-1111-111111111111")) + .projectKey("P3RO01") + .status(Status.RUNNING) + .build(); + CreateProjectResponse apiResponse = new CreateProjectResponse(); + apiResponse.setStatus("Running"); + apiResponse.setProjectFlavor("REGULAR"); + + when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); + when(projectCreationCommandBuilder.buildForRegistration(request, clientApp)).thenReturn(command); + when(projectMapper.toServiceRequest(command)).thenReturn(serviceRequest); + when(projectService.saveProject(serviceRequest)).thenReturn(projectResponse); + when(projectCreationResponseMapper.toSuccessResponse(command, projectResponse)).thenReturn(apiResponse); + + CreateProjectResponse result = sut.createProject(request, CLIENT_ID); + + assertEquals("Running", result.getStatus()); + assertEquals("REGULAR", result.getProjectFlavor()); + verify(projectCreationCommandBuilder).buildForRegistration(request, clientApp); + verify(projectMapper).toServiceRequest(command); + verify(automationParametersMapper, never()) + .toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111"); + verify(automationPlatformService, never()).executeWorkflow(anyString(), anyMap()); + verify(projectCreationResponseMapper).toSuccessResponse(command, projectResponse); + } + + @Test + void create_project_throws_project_creation_exception_when_automation_is_not_successful() { + CreateProjectRequest request = new CreateProjectRequest(); + ClientAppEntity clientApp = ClientAppEntity.builder().clientId(CLIENT_ID).build(); + ProjectCreationCommand command = new ProjectCreationCommand( + "DLSS01", "name", "desc", "DLSS", "CI-001", "eu", "x2test", "owner", CLIENT_ID); + + when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); + when(projectCreationCommandBuilder.buildForCreation(request, clientApp)).thenReturn(command); + when(projectMapper.toServiceRequest(command)).thenReturn(new ProjectRequest()); + when(projectService.saveProject(any(ProjectRequest.class))) + .thenReturn(ProjectResponse.builder() + .projectId(UUID.fromString("11111111-1111-1111-1111-111111111111")) + .projectKey("DLSS01") + .status(Status.PENDING) + .build()); + when(automationParametersMapper.toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111")) + .thenReturn(Map.of()); + when(automationPlatformService.executeWorkflow(anyString(), anyMap())) + .thenReturn(AutomationExecutionResult.failure("job-1", "error", "workflow failed")); + + assertThrows(ProjectCreationException.class, () -> sut.createProject(request, CLIENT_ID)); + } + + @Test + void get_project_returns_mapped_response_when_service_returns_project() { + ProjectResponse serviceResponse = ProjectResponse.builder() + .projectKey("PROJ01") + .status(Status.RUNNING) + .build(); + CreateProjectResponse mappedResponse = new CreateProjectResponse(); + mappedResponse.setProjectKey("PROJ01"); + mappedResponse.setStatus("Running"); + + when(projectService.getProject("PROJ01")).thenReturn(serviceResponse); + when(projectMapper.toApiResponse(serviceResponse)).thenReturn(mappedResponse); + + CreateProjectResponse result = sut.getProject("PROJ01"); + + assertEquals("PROJ01", result.getProjectKey()); + assertEquals("Running", result.getStatus()); + } + + @Test + void get_project_returns_null_when_service_returns_null() { + when(projectService.getProject("UNKNOWN")).thenReturn(null); + when(projectMapper.toApiResponse(null)).thenReturn(null); + + CreateProjectResponse result = sut.getProject("UNKNOWN"); + + assertNull(result); + } +} diff --git a/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/ProjectExistenceService.java b/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/ProjectExistenceService.java index 3933043..a1a34b9 100644 --- a/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/ProjectExistenceService.java +++ b/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/ProjectExistenceService.java @@ -1,10 +1,12 @@ -package org.opendevstack.apiservice.serviceproject.service; - -import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; - -public interface ProjectExistenceService { - - boolean isProjectFound(String projectKey) throws ProjectExistenceServiceException; - - boolean isProjectFoundByName(String projecName) throws ProjectExistenceServiceException; -} +package org.opendevstack.apiservice.serviceproject.service; + +import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; + +public interface ProjectExistenceService { + + boolean isProjectFound(String projectKey) throws ProjectExistenceServiceException; + + boolean isProjectFoundByName(String projecName) throws ProjectExistenceServiceException; + + boolean isProjectFoundInCollection(String projectKey) throws ProjectExistenceServiceException; +} diff --git a/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/impl/ProjectExistenceServiceImpl.java b/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/impl/ProjectExistenceServiceImpl.java index c576256..dde0b6f 100644 --- a/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/impl/ProjectExistenceServiceImpl.java +++ b/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/impl/ProjectExistenceServiceImpl.java @@ -1,92 +1,97 @@ -package org.opendevstack.apiservice.serviceproject.service.impl; - -import org.opendevstack.apiservice.externalservice.bitbucket.exception.BitbucketException; -import org.opendevstack.apiservice.externalservice.bitbucket.service.BitbucketService; -import org.opendevstack.apiservice.externalservice.jira.exception.JiraException; -import org.opendevstack.apiservice.externalservice.jira.service.JiraService; -import org.opendevstack.apiservice.externalservice.ocp.exception.OpenshiftException; -import org.opendevstack.apiservice.externalservice.ocp.service.OpenshiftService; -import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; -import org.opendevstack.apiservice.serviceproject.service.ProjectExistenceService; -import org.opendevstack.apiservice.serviceproject.service.ProjectService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import lombok.extern.slf4j.Slf4j; - -import java.util.Comparator; -import java.util.Set; - -@Slf4j -@Service -public class ProjectExistenceServiceImpl implements ProjectExistenceService { - - private final BitbucketService bitbucketService; - private final JiraService jiraService; - private final OpenshiftService openshiftService; - private final ProjectService projectService; - - @Autowired - public ProjectExistenceServiceImpl(BitbucketService bitbucketService, JiraService jiraService, - OpenshiftService openshiftService, ProjectService projectService) { - this.bitbucketService = bitbucketService; - this.jiraService = jiraService; - this.openshiftService = openshiftService; - this.projectService = projectService; - } - @Override - public boolean isProjectFound(String projectKey) throws ProjectExistenceServiceException { - try { - if (existsInCollection(projectKey)) return true; - if (existsInAnyBitbucketInstance(projectKey)) return true; - if (existsInAnyJiraInstance(projectKey)) return true; - return existsInAnyOpenshift(projectKey); - } catch (BitbucketException e) { - throw new ProjectExistenceServiceException("Failed to check project in Bitbucket", e); - } catch (JiraException e) { - throw new ProjectExistenceServiceException("Failed to check project in Jira", e); - } catch (OpenshiftException e) { - throw new ProjectExistenceServiceException("Failed to check project in Openshift", e); - } catch (RuntimeException e) { - log.error("Unexpected error while checking project existence for key '{}'", projectKey, e); - throw new ProjectExistenceServiceException("Unexpected error while checking project existence", e); - } - } - - @Override - public boolean isProjectFoundByName(String projecName) throws ProjectExistenceServiceException { - return !projectService.findProjectsByName(projecName).isEmpty(); - } - - private boolean existsInCollection(String projectKey) { - return projectService.getProject(projectKey) != null; - } - - private boolean existsInAnyBitbucketInstance(String projectKey) throws BitbucketException { - Set instances = bitbucketService.getAvailableInstances(); - for (String instance : instances.stream().sorted(Comparator.naturalOrder()).toList()) { - if (bitbucketService.projectExists(instance, projectKey)) return true; - } - return false; - } - - private boolean existsInAnyJiraInstance(String projectKey) throws JiraException { - Set instances = jiraService.getAvailableInstances(); - if (instances == null || instances.isEmpty()) { - return jiraService.projectExists(projectKey); - } - for (String instance : instances.stream().sorted(Comparator.naturalOrder()).toList()) { - if (jiraService.projectExists(instance, projectKey)) return true; - } - return false; - } - - private boolean existsInAnyOpenshift(String projectKey) throws OpenshiftException { - Set instances = openshiftService.getAvailableInstances(); - if (instances == null || instances.isEmpty()) return false; - for (String instance : instances.stream().sorted(Comparator.naturalOrder()).toList()) { - if (openshiftService.projectExists(instance, projectKey)) return true; - } - return false; - } -} +package org.opendevstack.apiservice.serviceproject.service.impl; + +import org.opendevstack.apiservice.externalservice.bitbucket.exception.BitbucketException; +import org.opendevstack.apiservice.externalservice.bitbucket.service.BitbucketService; +import org.opendevstack.apiservice.externalservice.jira.exception.JiraException; +import org.opendevstack.apiservice.externalservice.jira.service.JiraService; +import org.opendevstack.apiservice.externalservice.ocp.exception.OpenshiftException; +import org.opendevstack.apiservice.externalservice.ocp.service.OpenshiftService; +import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; +import org.opendevstack.apiservice.serviceproject.service.ProjectExistenceService; +import org.opendevstack.apiservice.serviceproject.service.ProjectService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import lombok.extern.slf4j.Slf4j; + +import java.util.Comparator; +import java.util.Set; + +@Slf4j +@Service +public class ProjectExistenceServiceImpl implements ProjectExistenceService { + + private final BitbucketService bitbucketService; + private final JiraService jiraService; + private final OpenshiftService openshiftService; + private final ProjectService projectService; + + @Autowired + public ProjectExistenceServiceImpl(BitbucketService bitbucketService, JiraService jiraService, + OpenshiftService openshiftService, ProjectService projectService) { + this.bitbucketService = bitbucketService; + this.jiraService = jiraService; + this.openshiftService = openshiftService; + this.projectService = projectService; + } + @Override + public boolean isProjectFound(String projectKey) throws ProjectExistenceServiceException { + try { + if (existsInCollection(projectKey)) return true; + if (existsInAnyBitbucketInstance(projectKey)) return true; + if (existsInAnyJiraInstance(projectKey)) return true; + return existsInAnyOpenshift(projectKey); + } catch (BitbucketException e) { + throw new ProjectExistenceServiceException("Failed to check project in Bitbucket", e); + } catch (JiraException e) { + throw new ProjectExistenceServiceException("Failed to check project in Jira", e); + } catch (OpenshiftException e) { + throw new ProjectExistenceServiceException("Failed to check project in Openshift", e); + } catch (RuntimeException e) { + log.error("Unexpected error while checking project existence for key '{}'", projectKey, e); + throw new ProjectExistenceServiceException("Unexpected error while checking project existence", e); + } + } + + @Override + public boolean isProjectFoundByName(String projecName) throws ProjectExistenceServiceException { + return !projectService.findProjectsByName(projecName).isEmpty(); + } + + @Override + public boolean isProjectFoundInCollection(String projectKey) throws ProjectExistenceServiceException { + return existsInCollection(projectKey); + } + + private boolean existsInCollection(String projectKey) { + return projectService.getProject(projectKey) != null; + } + + private boolean existsInAnyBitbucketInstance(String projectKey) throws BitbucketException { + Set instances = bitbucketService.getAvailableInstances(); + for (String instance : instances.stream().sorted(Comparator.naturalOrder()).toList()) { + if (bitbucketService.projectExists(instance, projectKey)) return true; + } + return false; + } + + private boolean existsInAnyJiraInstance(String projectKey) throws JiraException { + Set instances = jiraService.getAvailableInstances(); + if (instances == null || instances.isEmpty()) { + return jiraService.projectExists(projectKey); + } + for (String instance : instances.stream().sorted(Comparator.naturalOrder()).toList()) { + if (jiraService.projectExists(instance, projectKey)) return true; + } + return false; + } + + private boolean existsInAnyOpenshift(String projectKey) throws OpenshiftException { + Set instances = openshiftService.getAvailableInstances(); + if (instances == null || instances.isEmpty()) return false; + for (String instance : instances.stream().sorted(Comparator.naturalOrder()).toList()) { + if (openshiftService.projectExists(instance, projectKey)) return true; + } + return false; + } +} From f64f19c804739b764d42c854ef79bd6fd6263a22 Mon Sep 17 00:00:00 2001 From: Joselee2908 Date: Wed, 13 May 2026 17:41:57 +0200 Subject: [PATCH 2/3] Revert "fix(api-project): fixed registration to only check collection for already existence" This reverts commit 225e512169f7e7e92ff5348be9ae1dfdc10e5282. --- .../impl/ProjectCreationCommandBuilder.java | 355 +++++++------- .../facade/impl/ProjectsFacadeImpl.java | 245 +++++----- .../ProjectCreationCommandBuilderTest.java | 448 ++++++++---------- .../facade/impl/ProjectsFacadeImplTest.java | 422 ++++++++--------- .../service/ProjectExistenceService.java | 22 +- .../impl/ProjectExistenceServiceImpl.java | 189 ++++---- 6 files changed, 805 insertions(+), 876 deletions(-) diff --git a/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilder.java b/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilder.java index 46eab72..15866ca 100644 --- a/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilder.java +++ b/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilder.java @@ -1,193 +1,162 @@ -package org.opendevstack.apiservice.project.facade.impl; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.logging.log4j.util.Strings; -import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; -import org.opendevstack.apiservice.persistence.entity.ClientAppProjectFlavorEntity; -import org.opendevstack.apiservice.project.exception.ErrorKey; -import org.opendevstack.apiservice.project.exception.ProjectAlreadyExistsException; -import org.opendevstack.apiservice.project.exception.ProjectCreationException; -import org.opendevstack.apiservice.project.exception.ProjectValidationException; -import org.opendevstack.apiservice.project.model.CreateProjectRequest; -import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; -import org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException; -import org.opendevstack.apiservice.serviceproject.service.GenerateProjectKeyService; -import org.opendevstack.apiservice.serviceproject.service.ProjectExistenceService; -import org.springframework.stereotype.Component; - -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; - -@Component -@RequiredArgsConstructor -@Slf4j -public class ProjectCreationCommandBuilder { - - private final GenerateProjectKeyService generateProjectKeyService; - - private final ProjectExistenceService projectExistenceService; - - public ProjectCreationCommand buildForCreation(CreateProjectRequest request, ClientAppEntity clientApp) { - ClientAppProjectFlavorEntity flavor = resolveFlavor(request, clientApp); - String projectKey = resolveProjectKey(request.getProjectKey(), flavor); - - return build(request, flavor, projectKey, clientApp); - } - - public ProjectCreationCommand buildForRegistration(CreateProjectRequest request, ClientAppEntity clientApp) { - ClientAppProjectFlavorEntity flavor = resolveFlavor(request, clientApp); - String projectKey = resolveProjectKeyForRegistration(request.getProjectKey(), flavor); - - return build(request, flavor, projectKey, clientApp); - } - - private ProjectCreationCommand build(CreateProjectRequest request, ClientAppProjectFlavorEntity flavor, String projectKey, ClientAppEntity clientApp) { - String projectFlavor = firstNonBlank(request.getProjectFlavor(), flavor.getName()); - String configurationItem = firstNonBlank(request.getConfigurationItem(), flavor.getConfigItem()); - String owner = firstNonBlank(request.getOwner(), flavor.getProjectOwner()); - String x2account = firstNonBlank(request.getX2OdsAccount(), flavor.getServiceAccount()); - String location = firstNonBlank(request.getLocation(), flavor.getLocation()); - String projectName = resolveProjectName(request.getProjectName(), projectKey); - String projectDescription = firstNonBlank(request.getProjectDescription(), "project " + projectFlavor); - - return new ProjectCreationCommand( - projectKey, - projectName, - projectDescription, - projectFlavor, - configurationItem, - location, - x2account, - owner, - clientApp.getId()); - } - - private ClientAppProjectFlavorEntity resolveFlavor(CreateProjectRequest request, ClientAppEntity clientApp) { - List flavors = clientApp.getProjectFlavors(); - - if (flavors == null || flavors.isEmpty()) { - log.warn("ClientApp '{}' has no project flavors configured", clientApp.getClientId()); - throw new ProjectValidationException(ErrorKey.INVALID_PROJECT_FLAVOR); - } - - if (Strings.isNotEmpty(request.getProjectFlavor())) { - return resolveByFlavorName(request.getProjectFlavor(), flavors, clientApp.getClientId()); - } - - if (Strings.isNotEmpty(request.getConfigurationItem())) { - return resolveByConfigurationItem(request.getConfigurationItem(), flavors, clientApp.getClientId()); - } - - throw new ProjectValidationException(ErrorKey.BAD_REQUEST_FLAVOR_CONFIG_ITEM); - } - - private ClientAppProjectFlavorEntity resolveByFlavorName( - String flavorName, List flavors, UUID clientId) { - return flavors.stream() - .filter(f -> flavorName.equals(f.getName())) - .findFirst() - .orElseThrow(() -> { - log.warn("Flavor '{}' is not configured for clientApp '{}'", flavorName, clientId); - return new ProjectValidationException(ErrorKey.INVALID_PROJECT_FLAVOR); - }); - } - - private ClientAppProjectFlavorEntity resolveByConfigurationItem( - String configurationItem, List flavors, UUID clientId) { - List matchingFlavors = flavors.stream() - .filter(f -> configurationItem.equals(f.getConfigItem())) - .toList(); - - if (matchingFlavors.size() != 1) { - log.warn("ConfigItem '{}' does not match exactly one flavor for clientApp '{}'", - configurationItem, clientId); - String message = MessageFormat. - format("Not exists a project flavor configured for the Config Item {0}. " + - "To create a project under {0} the projectFlavor parameter is mandatory.", - configurationItem); - throw new ProjectValidationException(ErrorKey.INVALID_CONFIG_ITEM, message); - } - - ClientAppProjectFlavorEntity matchedFlavor = matchingFlavors.getFirst(); - - if (!isAllowedConfigItem(configurationItem, matchedFlavor)) { - log.warn("ConfigItem '{}' is not in the allowed list for flavor '{}' of clientApp '{}'", - configurationItem, matchedFlavor.getName(), clientId); - throw new ProjectValidationException(ErrorKey.INVALID_CONFIG_ITEM); - } - - return matchedFlavor; - } - - private String resolveProjectKey(String existingProjectKey, ClientAppProjectFlavorEntity flavor) { - try { - if (Strings.isNotEmpty(existingProjectKey)) { - if (!projectExistenceService.isProjectFound(existingProjectKey)) { - return existingProjectKey; - } - - throw new ProjectAlreadyExistsException(ErrorKey.PROJECT_ALREADY_EXISTS); - } - - String pattern = flavor.getProjectKeyPattern(); - - return generateProjectKeyService.generateProjectKey(pattern); - } catch (ProjectKeyGenerationException e) { - throw new ProjectCreationException("Error generating the project key", e); - } catch (ProjectExistenceServiceException e) { - throw new ProjectCreationException("Error checking if the generated key exists: " + e.getMessage(), e); - } - } - - private String resolveProjectKeyForRegistration(String existingProjectKey, ClientAppProjectFlavorEntity flavor) { - try { - if (Strings.isNotEmpty(existingProjectKey)) { - if (!projectExistenceService.isProjectFoundInCollection(existingProjectKey)) { - return existingProjectKey; - } - - throw new ProjectAlreadyExistsException(ErrorKey.PROJECT_ALREADY_EXISTS); - } - - String pattern = flavor.getProjectKeyPattern(); - - return generateProjectKeyService.generateProjectKey(pattern); - } catch (ProjectKeyGenerationException e) { - throw new ProjectCreationException("Error generating the project key", e); - } catch (ProjectExistenceServiceException e) { - throw new ProjectCreationException("Error checking if the generated key exists: " + e.getMessage(), e); - } - } - - private boolean isAllowedConfigItem(String configurationItem, ClientAppProjectFlavorEntity flavor) { - String[] allowedConfigItems = flavor.getAllowedConfigItems(); - - if (allowedConfigItems == null || allowedConfigItems.length == 0) { - return true; - } - - return Arrays.asList(allowedConfigItems).contains(configurationItem); - } - - private String firstNonBlank(String preferred, String fallback) { - return Strings.isNotEmpty(preferred) ? preferred : fallback; - } - - private String resolveProjectName(String preferred, String fallback) { - if (!Strings.isEmpty(preferred)) { - try { - if (projectExistenceService.isProjectFoundByName(preferred)) { - throw new ProjectAlreadyExistsException(ErrorKey.PROJECT_SAME_PROJECT_NAME_ALREADY_EXISTS); - } - } catch (ProjectExistenceServiceException e) { - throw new ProjectCreationException("Error checking if project name already exists: " + e.getMessage(), e); - } - } - - return firstNonBlank(preferred, fallback); - } -} - +package org.opendevstack.apiservice.project.facade.impl; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.logging.log4j.util.Strings; +import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; +import org.opendevstack.apiservice.persistence.entity.ClientAppProjectFlavorEntity; +import org.opendevstack.apiservice.project.exception.ErrorKey; +import org.opendevstack.apiservice.project.exception.ProjectAlreadyExistsException; +import org.opendevstack.apiservice.project.exception.ProjectCreationException; +import org.opendevstack.apiservice.project.exception.ProjectValidationException; +import org.opendevstack.apiservice.project.model.CreateProjectRequest; +import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; +import org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException; +import org.opendevstack.apiservice.serviceproject.service.GenerateProjectKeyService; +import org.opendevstack.apiservice.serviceproject.service.ProjectExistenceService; +import org.springframework.stereotype.Component; + +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +@Slf4j +public class ProjectCreationCommandBuilder { + + private final GenerateProjectKeyService generateProjectKeyService; + + private final ProjectExistenceService projectExistenceService; + + public ProjectCreationCommand build(CreateProjectRequest request, ClientAppEntity clientApp) { + ClientAppProjectFlavorEntity flavor = resolveFlavor(request, clientApp); + + String projectFlavor = firstNonBlank(request.getProjectFlavor(), flavor.getName()); + String configurationItem = firstNonBlank(request.getConfigurationItem(), flavor.getConfigItem()); + String owner = firstNonBlank(request.getOwner(), flavor.getProjectOwner()); + String x2account = firstNonBlank(request.getX2OdsAccount(), flavor.getServiceAccount()); + String location = firstNonBlank(request.getLocation(), flavor.getLocation()); + String projectKey = resolveProjectKey(request.getProjectKey(), flavor); + String projectName = resolveProjectName(request.getProjectName(), projectKey); + String projectDescription = firstNonBlank(request.getProjectDescription(), "project " + projectFlavor); + + return new ProjectCreationCommand( + projectKey, + projectName, + projectDescription, + projectFlavor, + configurationItem, + location, + x2account, + owner, + clientApp.getId()); + } + + private ClientAppProjectFlavorEntity resolveFlavor(CreateProjectRequest request, ClientAppEntity clientApp) { + List flavors = clientApp.getProjectFlavors(); + + if (flavors == null || flavors.isEmpty()) { + log.warn("ClientApp '{}' has no project flavors configured", clientApp.getClientId()); + throw new ProjectValidationException(ErrorKey.INVALID_PROJECT_FLAVOR); + } + + if (Strings.isNotEmpty(request.getProjectFlavor())) { + return resolveByFlavorName(request.getProjectFlavor(), flavors, clientApp.getClientId()); + } + + if (Strings.isNotEmpty(request.getConfigurationItem())) { + return resolveByConfigurationItem(request.getConfigurationItem(), flavors, clientApp.getClientId()); + } + + throw new ProjectValidationException(ErrorKey.BAD_REQUEST_FLAVOR_CONFIG_ITEM); + } + + private ClientAppProjectFlavorEntity resolveByFlavorName( + String flavorName, List flavors, UUID clientId) { + return flavors.stream() + .filter(f -> flavorName.equals(f.getName())) + .findFirst() + .orElseThrow(() -> { + log.warn("Flavor '{}' is not configured for clientApp '{}'", flavorName, clientId); + return new ProjectValidationException(ErrorKey.INVALID_PROJECT_FLAVOR); + }); + } + + private ClientAppProjectFlavorEntity resolveByConfigurationItem( + String configurationItem, List flavors, UUID clientId) { + List matchingFlavors = flavors.stream() + .filter(f -> configurationItem.equals(f.getConfigItem())) + .toList(); + + if (matchingFlavors.size() != 1) { + log.warn("ConfigItem '{}' does not match exactly one flavor for clientApp '{}'", + configurationItem, clientId); + String message = MessageFormat. + format("Not exists a project flavor configured for the Config Item {0}. " + + "To create a project under {0} the projectFlavor parameter is mandatory.", + configurationItem); + throw new ProjectValidationException(ErrorKey.INVALID_CONFIG_ITEM, message); + } + + ClientAppProjectFlavorEntity matchedFlavor = matchingFlavors.getFirst(); + + if (!isAllowedConfigItem(configurationItem, matchedFlavor)) { + log.warn("ConfigItem '{}' is not in the allowed list for flavor '{}' of clientApp '{}'", + configurationItem, matchedFlavor.getName(), clientId); + throw new ProjectValidationException(ErrorKey.INVALID_CONFIG_ITEM); + } + + return matchedFlavor; + } + + private String resolveProjectKey(String existingProjectKey, ClientAppProjectFlavorEntity flavor) { + try { + if (Strings.isNotEmpty(existingProjectKey)) { + if (!projectExistenceService.isProjectFound(existingProjectKey)) { + return existingProjectKey; + } + + throw new ProjectAlreadyExistsException(ErrorKey.PROJECT_ALREADY_EXISTS); + } + + String pattern = flavor.getProjectKeyPattern(); + + return generateProjectKeyService.generateProjectKey(pattern); + } catch (ProjectKeyGenerationException e) { + throw new ProjectCreationException("Error generating the project key", e); + } catch (ProjectExistenceServiceException e) { + throw new ProjectCreationException("Error checking if the generated key exists: " + e.getMessage(), e); + } + } + + private boolean isAllowedConfigItem(String configurationItem, ClientAppProjectFlavorEntity flavor) { + String[] allowedConfigItems = flavor.getAllowedConfigItems(); + + if (allowedConfigItems == null || allowedConfigItems.length == 0) { + return true; + } + + return Arrays.asList(allowedConfigItems).contains(configurationItem); + } + + private String firstNonBlank(String preferred, String fallback) { + return Strings.isNotEmpty(preferred) ? preferred : fallback; + } + + private String resolveProjectName(String preferred, String fallback) { + if (!Strings.isEmpty(preferred)) { + try { + if (projectExistenceService.isProjectFoundByName(preferred)) { + throw new ProjectAlreadyExistsException(ErrorKey.PROJECT_SAME_PROJECT_NAME_ALREADY_EXISTS); + } + } catch (ProjectExistenceServiceException e) { + throw new ProjectCreationException("Error checking if project name already exists: " + e.getMessage(), e); + } + } + + return firstNonBlank(preferred, fallback); + } +} + diff --git a/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImpl.java b/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImpl.java index 88a6e20..746fcd4 100644 --- a/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImpl.java +++ b/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImpl.java @@ -1,124 +1,123 @@ -package org.opendevstack.apiservice.project.facade.impl; - -import lombok.extern.slf4j.Slf4j; -import org.opendevstack.apiservice.externalservice.aap.model.AutomationExecutionResult; -import org.opendevstack.apiservice.externalservice.aap.service.AutomationPlatformService; -import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; -import org.opendevstack.apiservice.project.exception.ProjectCreationException; -import org.opendevstack.apiservice.project.facade.ProjectsFacade; -import org.opendevstack.apiservice.project.mapper.AutomationParametersMapper; -import org.opendevstack.apiservice.project.mapper.ProjectCreationResponseMapper; -import org.opendevstack.apiservice.project.mapper.ProjectMapper; -import org.opendevstack.apiservice.project.model.CreateProjectRequest; -import org.opendevstack.apiservice.project.model.CreateProjectResponse; -import org.opendevstack.apiservice.project.service.ClientAppService; -import org.opendevstack.apiservice.serviceproject.model.ProjectRequest; -import org.opendevstack.apiservice.serviceproject.model.ProjectResponse; -import org.opendevstack.apiservice.serviceproject.model.Status; -import org.opendevstack.apiservice.serviceproject.service.ProjectService; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import java.util.Map; -import java.util.UUID; - -@Component("apiProjectFacadeImpl") -@Slf4j -public class ProjectsFacadeImpl implements ProjectsFacade { - - @Value("${apis.projects.ansible-workflow-name}") - private String createProjectWorkflow; - - private final ProjectService projectService; - - private final ProjectMapper projectMapper; - - private final AutomationParametersMapper automationParametersMapper; - - private final ProjectCreationResponseMapper projectCreationResponseMapper; - - private final ProjectCreationCommandBuilder projectCreationCommandBuilder; - - private final ClientAppService clientAppService; - - private final AutomationPlatformService automationPlatformService; - - public ProjectsFacadeImpl( - ProjectService projectService, - ProjectMapper projectMapper, - AutomationParametersMapper automationParametersMapper, - ProjectCreationResponseMapper projectCreationResponseMapper, - ProjectCreationCommandBuilder projectCreationCommandBuilder, - ClientAppService clientAppService, - AutomationPlatformService automationPlatformService) { - this.projectService = projectService; - this.projectMapper = projectMapper; - this.automationParametersMapper = automationParametersMapper; - this.projectCreationResponseMapper = projectCreationResponseMapper; - this.projectCreationCommandBuilder = projectCreationCommandBuilder; - this.clientAppService = clientAppService; - this.automationPlatformService = automationPlatformService; - } - - @Override - public CreateProjectResponse createProject(CreateProjectRequest request, UUID clientId) { - - ClientAppEntity clientApp = clientAppService.findByClientId(clientId); - - ProjectCreationCommand command; - ProjectResponse project; - - if (Boolean.TRUE.equals(request.getRegistrationOnly())) { - command = projectCreationCommandBuilder.buildForRegistration(request, clientApp); - project = registerProject(command); - } else { - command = projectCreationCommandBuilder.buildForCreation(request, clientApp); - project = createNewProject(command); - } - - return projectCreationResponseMapper.toSuccessResponse(command, project); - } - - @Override - public CreateProjectResponse getProject(String projectKey) { - return projectMapper.toApiResponse(projectService.getProject(projectKey)); - } - - private ProjectResponse registerProject(ProjectCreationCommand command) { - - ProjectRequest projectRequest = projectMapper.toServiceRequest(command); - projectRequest.setStatus(Status.RUNNING); - projectRequest.setProjectFlavor("REGULAR"); - - return projectService.saveProject(projectRequest); - } - - private ProjectResponse createNewProject(ProjectCreationCommand command) { - - ProjectRequest projectRequest = projectMapper.toServiceRequest(command); - projectRequest.setStatus(Status.PENDING); - - ProjectResponse project = projectService.saveProject(projectRequest); - - initializeProject(command, projectRequest, project); - - return project; - } - - private void initializeProject( - ProjectCreationCommand command, ProjectRequest projectRequest, ProjectResponse project) { - String projectId = project.getProjectId().toString(); - Map workflowParameters = automationParametersMapper.toWorkflowParameters(command, projectId); - - AutomationExecutionResult automationExecutionResult = automationPlatformService - .executeWorkflow(createProjectWorkflow, workflowParameters); - - if (!automationExecutionResult.isSuccessful()) { - projectRequest.setProjectId(project.getProjectId()); - projectRequest.setStatus(Status.FAILED); - projectService.saveProject(projectRequest); - throw new ProjectCreationException("Failed to create project: " - + automationExecutionResult.getErrorDetails()); - } - } +package org.opendevstack.apiservice.project.facade.impl; + +import lombok.extern.slf4j.Slf4j; +import org.opendevstack.apiservice.externalservice.aap.model.AutomationExecutionResult; +import org.opendevstack.apiservice.externalservice.aap.service.AutomationPlatformService; +import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; +import org.opendevstack.apiservice.project.exception.ProjectCreationException; +import org.opendevstack.apiservice.project.facade.ProjectsFacade; +import org.opendevstack.apiservice.project.mapper.AutomationParametersMapper; +import org.opendevstack.apiservice.project.mapper.ProjectCreationResponseMapper; +import org.opendevstack.apiservice.project.mapper.ProjectMapper; +import org.opendevstack.apiservice.project.model.CreateProjectRequest; +import org.opendevstack.apiservice.project.model.CreateProjectResponse; +import org.opendevstack.apiservice.project.service.ClientAppService; +import org.opendevstack.apiservice.serviceproject.model.ProjectRequest; +import org.opendevstack.apiservice.serviceproject.model.ProjectResponse; +import org.opendevstack.apiservice.serviceproject.model.Status; +import org.opendevstack.apiservice.serviceproject.service.ProjectService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.UUID; + +@Component("apiProjectFacadeImpl") +@Slf4j +public class ProjectsFacadeImpl implements ProjectsFacade { + + @Value("${apis.projects.ansible-workflow-name}") + private String createProjectWorkflow; + + private final ProjectService projectService; + + private final ProjectMapper projectMapper; + + private final AutomationParametersMapper automationParametersMapper; + + private final ProjectCreationResponseMapper projectCreationResponseMapper; + + private final ProjectCreationCommandBuilder projectCreationCommandBuilder; + + private final ClientAppService clientAppService; + + private final AutomationPlatformService automationPlatformService; + + public ProjectsFacadeImpl( + ProjectService projectService, + ProjectMapper projectMapper, + AutomationParametersMapper automationParametersMapper, + ProjectCreationResponseMapper projectCreationResponseMapper, + ProjectCreationCommandBuilder projectCreationCommandBuilder, + ClientAppService clientAppService, + AutomationPlatformService automationPlatformService) { + this.projectService = projectService; + this.projectMapper = projectMapper; + this.automationParametersMapper = automationParametersMapper; + this.projectCreationResponseMapper = projectCreationResponseMapper; + this.projectCreationCommandBuilder = projectCreationCommandBuilder; + this.clientAppService = clientAppService; + this.automationPlatformService = automationPlatformService; + } + + @Override + public CreateProjectResponse createProject(CreateProjectRequest request, UUID clientId) { + + ClientAppEntity clientApp = clientAppService.findByClientId(clientId); + + ProjectCreationCommand command = projectCreationCommandBuilder.build(request, clientApp); + + ProjectResponse project; + + if (Boolean.TRUE.equals(request.getRegistrationOnly())) { + project = registerProject(command); + } else { + project = createNewProject(command); + } + + return projectCreationResponseMapper.toSuccessResponse(command, project); + } + + @Override + public CreateProjectResponse getProject(String projectKey) { + return projectMapper.toApiResponse(projectService.getProject(projectKey)); + } + + private ProjectResponse registerProject(ProjectCreationCommand command) { + + ProjectRequest projectRequest = projectMapper.toServiceRequest(command); + projectRequest.setStatus(Status.RUNNING); + projectRequest.setProjectFlavor("REGULAR"); + + return projectService.saveProject(projectRequest); + } + + private ProjectResponse createNewProject(ProjectCreationCommand command) { + + ProjectRequest projectRequest = projectMapper.toServiceRequest(command); + projectRequest.setStatus(Status.PENDING); + + ProjectResponse project = projectService.saveProject(projectRequest); + + initializeProject(command, projectRequest, project); + + return project; + } + + private void initializeProject( + ProjectCreationCommand command, ProjectRequest projectRequest, ProjectResponse project) { + String projectId = project.getProjectId().toString(); + Map workflowParameters = automationParametersMapper.toWorkflowParameters(command, projectId); + + AutomationExecutionResult automationExecutionResult = automationPlatformService + .executeWorkflow(createProjectWorkflow, workflowParameters); + + if (!automationExecutionResult.isSuccessful()) { + projectRequest.setProjectId(project.getProjectId()); + projectRequest.setStatus(Status.FAILED); + projectService.saveProject(projectRequest); + throw new ProjectCreationException("Failed to create project: " + + automationExecutionResult.getErrorDetails()); + } + } } \ No newline at end of file diff --git a/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilderTest.java b/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilderTest.java index 377a4fa..0c71c77 100644 --- a/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilderTest.java +++ b/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilderTest.java @@ -1,240 +1,208 @@ -package org.opendevstack.apiservice.project.facade.impl; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; -import org.opendevstack.apiservice.persistence.entity.ClientAppProjectFlavorEntity; -import org.opendevstack.apiservice.project.exception.ErrorKey; -import org.opendevstack.apiservice.project.exception.ProjectAlreadyExistsException; -import org.opendevstack.apiservice.project.exception.ProjectCreationException; -import org.opendevstack.apiservice.project.exception.ProjectValidationException; -import org.opendevstack.apiservice.project.model.CreateProjectRequest; -import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; -import org.opendevstack.apiservice.serviceproject.service.GenerateProjectKeyService; -import org.opendevstack.apiservice.serviceproject.service.ProjectExistenceService; - -import java.util.List; -import java.util.UUID; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -class ProjectCreationCommandBuilderTest { - - private static final UUID CLIENT_ID = UUID.fromString("00000000-0000-0000-0000-000000000001"); - - @Mock - private GenerateProjectKeyService generateProjectKeyService; - - @Mock - private ProjectExistenceService projectExistenceService; - - private ProjectCreationCommandBuilder sut; - - private AutoCloseable mocks; - - @BeforeEach - void set_up() { - mocks = MockitoAnnotations.openMocks(this); - sut = new ProjectCreationCommandBuilder(generateProjectKeyService, projectExistenceService); - } - - @AfterEach - void tear_down() throws Exception { - mocks.close(); - } - - @Test - void build_for_creation_resolves_defaults_from_flavor_when_request_fields_are_missing() throws ProjectExistenceServiceException { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request("DLSS", null, "KEY01"); - request.setOwner(null); - request.setLocation(null); - - when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); - - ProjectCreationCommand result = sut.buildForCreation(request, clientApp); - - assertEquals("DLSS", result.getProjectFlavor()); - assertEquals("CI-001", result.getConfigurationItem()); - assertEquals("owner1", result.getOwner()); - assertEquals("eu", result.getLocation()); - assertEquals("KEY01", result.getProjectKey()); - } - - @Test - void build_for_creation_resolves_flavor_from_configuration_item_when_flavor_is_not_provided() throws ProjectExistenceServiceException { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request(null, "CI-001", "KEY01"); - - when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); - - ProjectCreationCommand result = sut.buildForCreation(request, clientApp); - - assertEquals("DLSS", result.getProjectFlavor()); - assertEquals("CI-001", result.getConfigurationItem()); - } - - @Test - void build_for_creation_generates_project_key_when_request_project_key_is_null() throws ProjectExistenceServiceException, org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request("DLSS", null, null); - - when(generateProjectKeyService.generateProjectKey("DLSS%06d")).thenReturn("DLSS000001"); - when(projectExistenceService.isProjectFound("DLSS000001")).thenReturn(false); - - ProjectCreationCommand result = sut.buildForCreation(request, clientApp); - - assertEquals("DLSS000001", result.getProjectKey()); - verify(generateProjectKeyService).generateProjectKey("DLSS%06d"); - } - - @Test - void build_for_creation_throws_project_already_exists_exception_when_project_key_already_exists() throws ProjectExistenceServiceException { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request("DLSS", null, "KEY01"); - - when(projectExistenceService.isProjectFound("KEY01")).thenReturn(true); - - ProjectAlreadyExistsException ex = assertThrows(ProjectAlreadyExistsException.class, - () -> sut.buildForCreation(request, clientApp)); - assertEquals(ErrorKey.PROJECT_ALREADY_EXISTS, ex.getErrorKey()); - } - - @Test - void build_for_creation_throws_validation_exception_when_flavor_and_config_item_are_missing() { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request(null, null, "KEY01"); - - ProjectValidationException ex = assertThrows(ProjectValidationException.class, - () -> sut.buildForCreation(request, clientApp)); - assertEquals(ErrorKey.BAD_REQUEST_FLAVOR_CONFIG_ITEM, ex.getErrorKey()); - } - - @Test - void build_for_creation_throws_validation_exception_when_configuration_item_matches_multiple_flavors() { - ClientAppEntity clientApp = build_client_app(List.of( - build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"), - build_flavor("AMP", "CI-001", new String[] {}, "eu", "owner2"))); - CreateProjectRequest request = build_request(null, "CI-001", "KEY01"); - - ProjectValidationException ex = assertThrows(ProjectValidationException.class, - () -> sut.buildForCreation(request, clientApp)); - assertEquals(ErrorKey.INVALID_CONFIG_ITEM, ex.getErrorKey()); - } - - @Test - void build_for_creation_throws_project_key_generation_exception_when_generation_fails() throws Exception { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request("DLSS", null, null); - - when(generateProjectKeyService.generateProjectKey("DLSS%06d")) - .thenThrow(new org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException("fail")); - - assertThrows(ProjectCreationException.class, () -> sut.buildForCreation(request, clientApp)); - } - - @Test - void build_for_creation_throws_project_already_exists_exception_when_project_name_already_exists() throws Exception { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request("DLSS", null, "KEY01"); - request.setProjectName("Existing Project"); - - when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); - when(projectExistenceService.isProjectFoundByName("Existing Project")).thenReturn(true); - - assertThrows(ProjectAlreadyExistsException.class, () -> sut.buildForCreation(request, clientApp)); - } - - @Test - void build_for_creation_throws_project_creation_exception_when_project_name_check_fails() throws Exception { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request("DLSS", null, "KEY01"); - request.setProjectName("Any Project"); - - when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); - when(projectExistenceService.isProjectFoundByName("Any Project")) - .thenThrow(new ProjectExistenceServiceException("lookup failed")); - - assertThrows(ProjectCreationException.class, () -> sut.buildForCreation(request, clientApp)); - } - - @Test - void build_for_registration_resolves_defaults_from_flavor_when_request_fields_are_missing() throws ProjectExistenceServiceException { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request("DLSS", null, "KEY01"); - request.setOwner(null); - request.setLocation(null); - - when(projectExistenceService.isProjectFoundInCollection("KEY01")).thenReturn(false); - - ProjectCreationCommand result = sut.buildForRegistration(request, clientApp); - - assertEquals("DLSS", result.getProjectFlavor()); - assertEquals("CI-001", result.getConfigurationItem()); - assertEquals("owner1", result.getOwner()); - assertEquals("eu", result.getLocation()); - assertEquals("KEY01", result.getProjectKey()); - } - - @Test - void build_for_registration_throws_project_already_exists_exception_when_project_key_already_exists() throws ProjectExistenceServiceException { - ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); - ClientAppEntity clientApp = build_client_app(List.of(flavor)); - CreateProjectRequest request = build_request("DLSS", null, "KEY01"); - - when(projectExistenceService.isProjectFoundInCollection("KEY01")).thenReturn(true); - - ProjectAlreadyExistsException ex = assertThrows(ProjectAlreadyExistsException.class, - () -> sut.buildForRegistration(request, clientApp)); - assertEquals(ErrorKey.PROJECT_ALREADY_EXISTS, ex.getErrorKey()); - } - - private CreateProjectRequest build_request(String flavor, String configItem, String projectKey) { - CreateProjectRequest request = new CreateProjectRequest(); - request.setProjectKey(projectKey); - request.setProjectFlavor(flavor); - request.setConfigurationItem(configItem); - request.setProjectName("Test Project"); - request.setProjectDescription("A test project"); - request.setOwner("testowner"); - request.setLocation("eu"); - request.setX2OdsAccount("x2test"); - return request; - } - - private ClientAppEntity build_client_app(List flavors) { - ClientAppEntity entity = ClientAppEntity.builder() - .clientId(CLIENT_ID) - .clientName("Test App") - .build(); - entity.setProjectFlavors(flavors); - return entity; - } - - private ClientAppProjectFlavorEntity build_flavor( - String name, String configItem, String[] allowedConfigItems, String location, String projectOwner) { - return ClientAppProjectFlavorEntity.builder() - .name(name) - .configItem(configItem) - .allowedConfigItems(allowedConfigItems) - .projectKeyPattern(name + "%06d") - .location(location) - .projectOwner(projectOwner) - .build(); - } -} +package org.opendevstack.apiservice.project.facade.impl; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; +import org.opendevstack.apiservice.persistence.entity.ClientAppProjectFlavorEntity; +import org.opendevstack.apiservice.project.exception.ErrorKey; +import org.opendevstack.apiservice.project.exception.ProjectAlreadyExistsException; +import org.opendevstack.apiservice.project.exception.ProjectCreationException; +import org.opendevstack.apiservice.project.exception.ProjectValidationException; +import org.opendevstack.apiservice.project.model.CreateProjectRequest; +import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; +import org.opendevstack.apiservice.serviceproject.service.GenerateProjectKeyService; +import org.opendevstack.apiservice.serviceproject.service.ProjectExistenceService; + +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class ProjectCreationCommandBuilderTest { + + private static final UUID CLIENT_ID = UUID.fromString("00000000-0000-0000-0000-000000000001"); + + @Mock + private GenerateProjectKeyService generateProjectKeyService; + + @Mock + private ProjectExistenceService projectExistenceService; + + private ProjectCreationCommandBuilder sut; + + private AutoCloseable mocks; + + @BeforeEach + void set_up() { + mocks = MockitoAnnotations.openMocks(this); + sut = new ProjectCreationCommandBuilder(generateProjectKeyService, projectExistenceService); + } + + @AfterEach + void tear_down() throws Exception { + mocks.close(); + } + + @Test + void build_resolves_defaults_from_flavor_when_request_fields_are_missing() throws ProjectExistenceServiceException { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, "KEY01"); + request.setOwner(null); + request.setLocation(null); + + when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); + + ProjectCreationCommand result = sut.build(request, clientApp); + + assertEquals("DLSS", result.getProjectFlavor()); + assertEquals("CI-001", result.getConfigurationItem()); + assertEquals("owner1", result.getOwner()); + assertEquals("eu", result.getLocation()); + assertEquals("KEY01", result.getProjectKey()); + } + + @Test + void build_resolves_flavor_from_configuration_item_when_flavor_is_not_provided() throws ProjectExistenceServiceException { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request(null, "CI-001", "KEY01"); + + when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); + + ProjectCreationCommand result = sut.build(request, clientApp); + + assertEquals("DLSS", result.getProjectFlavor()); + assertEquals("CI-001", result.getConfigurationItem()); + } + + @Test + void build_generates_project_key_when_request_project_key_is_null() throws ProjectExistenceServiceException, org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, null); + + when(generateProjectKeyService.generateProjectKey("DLSS%06d")).thenReturn("DLSS000001"); + when(projectExistenceService.isProjectFound("DLSS000001")).thenReturn(false); + + ProjectCreationCommand result = sut.build(request, clientApp); + + assertEquals("DLSS000001", result.getProjectKey()); + verify(generateProjectKeyService).generateProjectKey("DLSS%06d"); + } + + @Test + void build_throws_project_already_exists_exception_when_project_key_already_exists() throws ProjectExistenceServiceException { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, "KEY01"); + + when(projectExistenceService.isProjectFound("KEY01")).thenReturn(true); + + ProjectAlreadyExistsException ex = assertThrows(ProjectAlreadyExistsException.class, + () -> sut.build(request, clientApp)); + assertEquals(ErrorKey.PROJECT_ALREADY_EXISTS, ex.getErrorKey()); + } + + @Test + void build_throws_validation_exception_when_flavor_and_config_item_are_missing() { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request(null, null, "KEY01"); + + ProjectValidationException ex = assertThrows(ProjectValidationException.class, + () -> sut.build(request, clientApp)); + assertEquals(ErrorKey.BAD_REQUEST_FLAVOR_CONFIG_ITEM, ex.getErrorKey()); + } + + @Test + void build_throws_validation_exception_when_configuration_item_matches_multiple_flavors() { + ClientAppEntity clientApp = build_client_app(List.of( + build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"), + build_flavor("AMP", "CI-001", new String[] {}, "eu", "owner2"))); + CreateProjectRequest request = build_request(null, "CI-001", "KEY01"); + + ProjectValidationException ex = assertThrows(ProjectValidationException.class, + () -> sut.build(request, clientApp)); + assertEquals(ErrorKey.INVALID_CONFIG_ITEM, ex.getErrorKey()); + } + + @Test + void build_throws_project_key_generation_exception_when_generation_fails() throws Exception { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, null); + + when(generateProjectKeyService.generateProjectKey("DLSS%06d")) + .thenThrow(new org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException("fail")); + + assertThrows(ProjectCreationException.class, () -> sut.build(request, clientApp)); + } + + @Test + void build_throws_project_already_exists_exception_when_project_name_already_exists() throws Exception { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, "KEY01"); + request.setProjectName("Existing Project"); + + when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); + when(projectExistenceService.isProjectFoundByName("Existing Project")).thenReturn(true); + + assertThrows(ProjectAlreadyExistsException.class, () -> sut.build(request, clientApp)); + } + + @Test + void build_throws_project_creation_exception_when_project_name_check_fails() throws Exception { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, "KEY01"); + request.setProjectName("Any Project"); + + when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); + when(projectExistenceService.isProjectFoundByName("Any Project")) + .thenThrow(new ProjectExistenceServiceException("lookup failed")); + + assertThrows(ProjectCreationException.class, () -> sut.build(request, clientApp)); + } + + private CreateProjectRequest build_request(String flavor, String configItem, String projectKey) { + CreateProjectRequest request = new CreateProjectRequest(); + request.setProjectKey(projectKey); + request.setProjectFlavor(flavor); + request.setConfigurationItem(configItem); + request.setProjectName("Test Project"); + request.setProjectDescription("A test project"); + request.setOwner("testowner"); + request.setLocation("eu"); + request.setX2OdsAccount("x2test"); + return request; + } + + private ClientAppEntity build_client_app(List flavors) { + ClientAppEntity entity = ClientAppEntity.builder() + .clientId(CLIENT_ID) + .clientName("Test App") + .build(); + entity.setProjectFlavors(flavors); + return entity; + } + + private ClientAppProjectFlavorEntity build_flavor( + String name, String configItem, String[] allowedConfigItems, String location, String projectOwner) { + return ClientAppProjectFlavorEntity.builder() + .name(name) + .configItem(configItem) + .allowedConfigItems(allowedConfigItems) + .projectKeyPattern(name + "%06d") + .location(location) + .projectOwner(projectOwner) + .build(); + } +} diff --git a/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImplTest.java b/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImplTest.java index 595657a..dd622ef 100644 --- a/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImplTest.java +++ b/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImplTest.java @@ -1,211 +1,211 @@ -package org.opendevstack.apiservice.project.facade.impl; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendevstack.apiservice.externalservice.aap.model.AutomationExecutionResult; -import org.opendevstack.apiservice.externalservice.aap.service.AutomationPlatformService; -import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; -import org.opendevstack.apiservice.project.exception.ProjectCreationException; -import org.opendevstack.apiservice.project.mapper.AutomationParametersMapper; -import org.opendevstack.apiservice.project.mapper.ProjectCreationResponseMapper; -import org.opendevstack.apiservice.project.mapper.ProjectMapper; -import org.opendevstack.apiservice.project.model.CreateProjectRequest; -import org.opendevstack.apiservice.project.model.CreateProjectResponse; -import org.opendevstack.apiservice.project.service.ClientAppService; -import org.opendevstack.apiservice.serviceproject.model.ProjectRequest; -import org.opendevstack.apiservice.serviceproject.model.ProjectResponse; -import org.opendevstack.apiservice.serviceproject.model.Status; -import org.opendevstack.apiservice.serviceproject.service.ProjectService; - -import java.lang.reflect.Field; -import java.util.Map; -import java.util.UUID; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyMap; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -class ProjectsFacadeImplTest { - - private static final UUID CLIENT_ID = UUID.fromString("00000000-0000-0000-0000-000000000001"); - - @Mock - private ProjectService projectService; - - @Mock - private ProjectMapper projectMapper; - - @Mock - private AutomationParametersMapper automationParametersMapper; - - @Mock - private ProjectCreationResponseMapper projectCreationResponseMapper; - - @Mock - private ProjectCreationCommandBuilder projectCreationCommandBuilder; - - @Mock - private ClientAppService clientAppService; - - @Mock - private AutomationPlatformService automationPlatformService; - - private ProjectsFacadeImpl sut; - private AutoCloseable mocks; - - @BeforeEach - void set_up() throws Exception { - mocks = MockitoAnnotations.openMocks(this); - sut = new ProjectsFacadeImpl( - projectService, - projectMapper, - automationParametersMapper, - projectCreationResponseMapper, - projectCreationCommandBuilder, - clientAppService, - automationPlatformService); - - Field workflowField = ProjectsFacadeImpl.class.getDeclaredField("createProjectWorkflow"); - workflowField.setAccessible(true); - workflowField.set(sut, "create-project-workflow"); - } - - @AfterEach - void tear_down() throws Exception { - mocks.close(); - } - - @Test - void create_project_returns_success_response_when_automation_is_successful() { - CreateProjectRequest request = new CreateProjectRequest(); - ClientAppEntity clientApp = ClientAppEntity.builder().clientId(CLIENT_ID).build(); - ProjectCreationCommand command = new ProjectCreationCommand( - "DLSS01", "name", "desc", "DLSS", "CI-001", "eu", "x2test", "owner", CLIENT_ID); - ProjectRequest serviceRequest = new ProjectRequest(); - ProjectResponse projectResponse = ProjectResponse.builder() - .projectId(UUID.fromString("11111111-1111-1111-1111-111111111111")) - .projectKey("DLSS01") - .status(Status.PENDING) - .build(); - CreateProjectResponse apiResponse = new CreateProjectResponse(); - apiResponse.setStatus("Pending"); - apiResponse.setProjectFlavor("DLSS"); - - when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); - when(projectCreationCommandBuilder.buildForCreation(request, clientApp)).thenReturn(command); - when(projectMapper.toServiceRequest(command)).thenReturn(serviceRequest); - when(projectService.saveProject(serviceRequest)).thenReturn(projectResponse); - when(automationParametersMapper.toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111")) - .thenReturn(Map.of("project_key", "DLSS01")); - when(automationPlatformService.executeWorkflow(anyString(), anyMap())) - .thenReturn(AutomationExecutionResult.success("job-1", "ok")); - when(projectCreationResponseMapper.toSuccessResponse(command, projectResponse)).thenReturn(apiResponse); - - CreateProjectResponse result = sut.createProject(request, CLIENT_ID); - - assertEquals("Pending", result.getStatus()); - assertEquals("DLSS", result.getProjectFlavor()); - verify(projectCreationCommandBuilder).buildForCreation(request, clientApp); - verify(projectMapper).toServiceRequest(command); - verify(automationParametersMapper) - .toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111"); - verify(projectCreationResponseMapper).toSuccessResponse(command, projectResponse); - } - - @Test - void create_project_with_registrationOnly_returns_success_response_when_automation_is_not_executed() { - CreateProjectRequest request = new CreateProjectRequest(); - request.setRegistrationOnly(true); - ClientAppEntity clientApp = ClientAppEntity.builder().clientId(CLIENT_ID).build(); - ProjectCreationCommand command = new ProjectCreationCommand( - "P3RO01", "name", "desc", null, "CI-001", "eu", "x2test", "owner", CLIENT_ID); - ProjectRequest serviceRequest = new ProjectRequest(); - ProjectResponse projectResponse = ProjectResponse.builder() - .projectId(UUID.fromString("11111111-1111-1111-1111-111111111111")) - .projectKey("P3RO01") - .status(Status.RUNNING) - .build(); - CreateProjectResponse apiResponse = new CreateProjectResponse(); - apiResponse.setStatus("Running"); - apiResponse.setProjectFlavor("REGULAR"); - - when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); - when(projectCreationCommandBuilder.buildForRegistration(request, clientApp)).thenReturn(command); - when(projectMapper.toServiceRequest(command)).thenReturn(serviceRequest); - when(projectService.saveProject(serviceRequest)).thenReturn(projectResponse); - when(projectCreationResponseMapper.toSuccessResponse(command, projectResponse)).thenReturn(apiResponse); - - CreateProjectResponse result = sut.createProject(request, CLIENT_ID); - - assertEquals("Running", result.getStatus()); - assertEquals("REGULAR", result.getProjectFlavor()); - verify(projectCreationCommandBuilder).buildForRegistration(request, clientApp); - verify(projectMapper).toServiceRequest(command); - verify(automationParametersMapper, never()) - .toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111"); - verify(automationPlatformService, never()).executeWorkflow(anyString(), anyMap()); - verify(projectCreationResponseMapper).toSuccessResponse(command, projectResponse); - } - - @Test - void create_project_throws_project_creation_exception_when_automation_is_not_successful() { - CreateProjectRequest request = new CreateProjectRequest(); - ClientAppEntity clientApp = ClientAppEntity.builder().clientId(CLIENT_ID).build(); - ProjectCreationCommand command = new ProjectCreationCommand( - "DLSS01", "name", "desc", "DLSS", "CI-001", "eu", "x2test", "owner", CLIENT_ID); - - when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); - when(projectCreationCommandBuilder.buildForCreation(request, clientApp)).thenReturn(command); - when(projectMapper.toServiceRequest(command)).thenReturn(new ProjectRequest()); - when(projectService.saveProject(any(ProjectRequest.class))) - .thenReturn(ProjectResponse.builder() - .projectId(UUID.fromString("11111111-1111-1111-1111-111111111111")) - .projectKey("DLSS01") - .status(Status.PENDING) - .build()); - when(automationParametersMapper.toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111")) - .thenReturn(Map.of()); - when(automationPlatformService.executeWorkflow(anyString(), anyMap())) - .thenReturn(AutomationExecutionResult.failure("job-1", "error", "workflow failed")); - - assertThrows(ProjectCreationException.class, () -> sut.createProject(request, CLIENT_ID)); - } - - @Test - void get_project_returns_mapped_response_when_service_returns_project() { - ProjectResponse serviceResponse = ProjectResponse.builder() - .projectKey("PROJ01") - .status(Status.RUNNING) - .build(); - CreateProjectResponse mappedResponse = new CreateProjectResponse(); - mappedResponse.setProjectKey("PROJ01"); - mappedResponse.setStatus("Running"); - - when(projectService.getProject("PROJ01")).thenReturn(serviceResponse); - when(projectMapper.toApiResponse(serviceResponse)).thenReturn(mappedResponse); - - CreateProjectResponse result = sut.getProject("PROJ01"); - - assertEquals("PROJ01", result.getProjectKey()); - assertEquals("Running", result.getStatus()); - } - - @Test - void get_project_returns_null_when_service_returns_null() { - when(projectService.getProject("UNKNOWN")).thenReturn(null); - when(projectMapper.toApiResponse(null)).thenReturn(null); - - CreateProjectResponse result = sut.getProject("UNKNOWN"); - - assertNull(result); - } -} +package org.opendevstack.apiservice.project.facade.impl; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendevstack.apiservice.externalservice.aap.model.AutomationExecutionResult; +import org.opendevstack.apiservice.externalservice.aap.service.AutomationPlatformService; +import org.opendevstack.apiservice.persistence.entity.ClientAppEntity; +import org.opendevstack.apiservice.project.exception.ProjectCreationException; +import org.opendevstack.apiservice.project.mapper.AutomationParametersMapper; +import org.opendevstack.apiservice.project.mapper.ProjectCreationResponseMapper; +import org.opendevstack.apiservice.project.mapper.ProjectMapper; +import org.opendevstack.apiservice.project.model.CreateProjectRequest; +import org.opendevstack.apiservice.project.model.CreateProjectResponse; +import org.opendevstack.apiservice.project.service.ClientAppService; +import org.opendevstack.apiservice.serviceproject.model.ProjectRequest; +import org.opendevstack.apiservice.serviceproject.model.ProjectResponse; +import org.opendevstack.apiservice.serviceproject.model.Status; +import org.opendevstack.apiservice.serviceproject.service.ProjectService; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class ProjectsFacadeImplTest { + + private static final UUID CLIENT_ID = UUID.fromString("00000000-0000-0000-0000-000000000001"); + + @Mock + private ProjectService projectService; + + @Mock + private ProjectMapper projectMapper; + + @Mock + private AutomationParametersMapper automationParametersMapper; + + @Mock + private ProjectCreationResponseMapper projectCreationResponseMapper; + + @Mock + private ProjectCreationCommandBuilder projectCreationCommandBuilder; + + @Mock + private ClientAppService clientAppService; + + @Mock + private AutomationPlatformService automationPlatformService; + + private ProjectsFacadeImpl sut; + private AutoCloseable mocks; + + @BeforeEach + void set_up() throws Exception { + mocks = MockitoAnnotations.openMocks(this); + sut = new ProjectsFacadeImpl( + projectService, + projectMapper, + automationParametersMapper, + projectCreationResponseMapper, + projectCreationCommandBuilder, + clientAppService, + automationPlatformService); + + Field workflowField = ProjectsFacadeImpl.class.getDeclaredField("createProjectWorkflow"); + workflowField.setAccessible(true); + workflowField.set(sut, "create-project-workflow"); + } + + @AfterEach + void tear_down() throws Exception { + mocks.close(); + } + + @Test + void create_project_returns_success_response_when_automation_is_successful() { + CreateProjectRequest request = new CreateProjectRequest(); + ClientAppEntity clientApp = ClientAppEntity.builder().clientId(CLIENT_ID).build(); + ProjectCreationCommand command = new ProjectCreationCommand( + "DLSS01", "name", "desc", "DLSS", "CI-001", "eu", "x2test", "owner", CLIENT_ID); + ProjectRequest serviceRequest = new ProjectRequest(); + ProjectResponse projectResponse = ProjectResponse.builder() + .projectId(UUID.fromString("11111111-1111-1111-1111-111111111111")) + .projectKey("DLSS01") + .status(Status.PENDING) + .build(); + CreateProjectResponse apiResponse = new CreateProjectResponse(); + apiResponse.setStatus("Pending"); + apiResponse.setProjectFlavor("DLSS"); + + when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); + when(projectCreationCommandBuilder.build(request, clientApp)).thenReturn(command); + when(projectMapper.toServiceRequest(command)).thenReturn(serviceRequest); + when(projectService.saveProject(serviceRequest)).thenReturn(projectResponse); + when(automationParametersMapper.toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111")) + .thenReturn(Map.of("project_key", "DLSS01")); + when(automationPlatformService.executeWorkflow(anyString(), anyMap())) + .thenReturn(AutomationExecutionResult.success("job-1", "ok")); + when(projectCreationResponseMapper.toSuccessResponse(command, projectResponse)).thenReturn(apiResponse); + + CreateProjectResponse result = sut.createProject(request, CLIENT_ID); + + assertEquals("Pending", result.getStatus()); + assertEquals("DLSS", result.getProjectFlavor()); + verify(projectCreationCommandBuilder).build(request, clientApp); + verify(projectMapper).toServiceRequest(command); + verify(automationParametersMapper) + .toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111"); + verify(projectCreationResponseMapper).toSuccessResponse(command, projectResponse); + } + + @Test + void create_project_with_registrationOnly_returns_success_response_when_automation_is_not_executed() { + CreateProjectRequest request = new CreateProjectRequest(); + request.setRegistrationOnly(true); + ClientAppEntity clientApp = ClientAppEntity.builder().clientId(CLIENT_ID).build(); + ProjectCreationCommand command = new ProjectCreationCommand( + "P3RO01", "name", "desc", null, "CI-001", "eu", "x2test", "owner", CLIENT_ID); + ProjectRequest serviceRequest = new ProjectRequest(); + ProjectResponse projectResponse = ProjectResponse.builder() + .projectId(UUID.fromString("11111111-1111-1111-1111-111111111111")) + .projectKey("P3RO01") + .status(Status.RUNNING) + .build(); + CreateProjectResponse apiResponse = new CreateProjectResponse(); + apiResponse.setStatus("Running"); + apiResponse.setProjectFlavor("REGULAR"); + + when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); + when(projectCreationCommandBuilder.build(request, clientApp)).thenReturn(command); + when(projectMapper.toServiceRequest(command)).thenReturn(serviceRequest); + when(projectService.saveProject(serviceRequest)).thenReturn(projectResponse); + when(projectCreationResponseMapper.toSuccessResponse(command, projectResponse)).thenReturn(apiResponse); + + CreateProjectResponse result = sut.createProject(request, CLIENT_ID); + + assertEquals("Running", result.getStatus()); + assertEquals("REGULAR", result.getProjectFlavor()); + verify(projectCreationCommandBuilder).build(request, clientApp); + verify(projectMapper).toServiceRequest(command); + verify(automationParametersMapper, never()) + .toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111"); + verify(automationPlatformService, never()).executeWorkflow(anyString(), anyMap()); + verify(projectCreationResponseMapper).toSuccessResponse(command, projectResponse); + } + + @Test + void create_project_throws_project_creation_exception_when_automation_is_not_successful() { + CreateProjectRequest request = new CreateProjectRequest(); + ClientAppEntity clientApp = ClientAppEntity.builder().clientId(CLIENT_ID).build(); + ProjectCreationCommand command = new ProjectCreationCommand( + "DLSS01", "name", "desc", "DLSS", "CI-001", "eu", "x2test", "owner", CLIENT_ID); + + when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); + when(projectCreationCommandBuilder.build(request, clientApp)).thenReturn(command); + when(projectMapper.toServiceRequest(command)).thenReturn(new ProjectRequest()); + when(projectService.saveProject(any(ProjectRequest.class))) + .thenReturn(ProjectResponse.builder() + .projectId(UUID.fromString("11111111-1111-1111-1111-111111111111")) + .projectKey("DLSS01") + .status(Status.PENDING) + .build()); + when(automationParametersMapper.toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111")) + .thenReturn(Map.of()); + when(automationPlatformService.executeWorkflow(anyString(), anyMap())) + .thenReturn(AutomationExecutionResult.failure("job-1", "error", "workflow failed")); + + assertThrows(ProjectCreationException.class, () -> sut.createProject(request, CLIENT_ID)); + } + + @Test + void get_project_returns_mapped_response_when_service_returns_project() { + ProjectResponse serviceResponse = ProjectResponse.builder() + .projectKey("PROJ01") + .status(Status.RUNNING) + .build(); + CreateProjectResponse mappedResponse = new CreateProjectResponse(); + mappedResponse.setProjectKey("PROJ01"); + mappedResponse.setStatus("Running"); + + when(projectService.getProject("PROJ01")).thenReturn(serviceResponse); + when(projectMapper.toApiResponse(serviceResponse)).thenReturn(mappedResponse); + + CreateProjectResponse result = sut.getProject("PROJ01"); + + assertEquals("PROJ01", result.getProjectKey()); + assertEquals("Running", result.getStatus()); + } + + @Test + void get_project_returns_null_when_service_returns_null() { + when(projectService.getProject("UNKNOWN")).thenReturn(null); + when(projectMapper.toApiResponse(null)).thenReturn(null); + + CreateProjectResponse result = sut.getProject("UNKNOWN"); + + assertNull(result); + } +} diff --git a/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/ProjectExistenceService.java b/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/ProjectExistenceService.java index a1a34b9..3933043 100644 --- a/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/ProjectExistenceService.java +++ b/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/ProjectExistenceService.java @@ -1,12 +1,10 @@ -package org.opendevstack.apiservice.serviceproject.service; - -import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; - -public interface ProjectExistenceService { - - boolean isProjectFound(String projectKey) throws ProjectExistenceServiceException; - - boolean isProjectFoundByName(String projecName) throws ProjectExistenceServiceException; - - boolean isProjectFoundInCollection(String projectKey) throws ProjectExistenceServiceException; -} +package org.opendevstack.apiservice.serviceproject.service; + +import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; + +public interface ProjectExistenceService { + + boolean isProjectFound(String projectKey) throws ProjectExistenceServiceException; + + boolean isProjectFoundByName(String projecName) throws ProjectExistenceServiceException; +} diff --git a/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/impl/ProjectExistenceServiceImpl.java b/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/impl/ProjectExistenceServiceImpl.java index dde0b6f..c576256 100644 --- a/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/impl/ProjectExistenceServiceImpl.java +++ b/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/impl/ProjectExistenceServiceImpl.java @@ -1,97 +1,92 @@ -package org.opendevstack.apiservice.serviceproject.service.impl; - -import org.opendevstack.apiservice.externalservice.bitbucket.exception.BitbucketException; -import org.opendevstack.apiservice.externalservice.bitbucket.service.BitbucketService; -import org.opendevstack.apiservice.externalservice.jira.exception.JiraException; -import org.opendevstack.apiservice.externalservice.jira.service.JiraService; -import org.opendevstack.apiservice.externalservice.ocp.exception.OpenshiftException; -import org.opendevstack.apiservice.externalservice.ocp.service.OpenshiftService; -import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; -import org.opendevstack.apiservice.serviceproject.service.ProjectExistenceService; -import org.opendevstack.apiservice.serviceproject.service.ProjectService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import lombok.extern.slf4j.Slf4j; - -import java.util.Comparator; -import java.util.Set; - -@Slf4j -@Service -public class ProjectExistenceServiceImpl implements ProjectExistenceService { - - private final BitbucketService bitbucketService; - private final JiraService jiraService; - private final OpenshiftService openshiftService; - private final ProjectService projectService; - - @Autowired - public ProjectExistenceServiceImpl(BitbucketService bitbucketService, JiraService jiraService, - OpenshiftService openshiftService, ProjectService projectService) { - this.bitbucketService = bitbucketService; - this.jiraService = jiraService; - this.openshiftService = openshiftService; - this.projectService = projectService; - } - @Override - public boolean isProjectFound(String projectKey) throws ProjectExistenceServiceException { - try { - if (existsInCollection(projectKey)) return true; - if (existsInAnyBitbucketInstance(projectKey)) return true; - if (existsInAnyJiraInstance(projectKey)) return true; - return existsInAnyOpenshift(projectKey); - } catch (BitbucketException e) { - throw new ProjectExistenceServiceException("Failed to check project in Bitbucket", e); - } catch (JiraException e) { - throw new ProjectExistenceServiceException("Failed to check project in Jira", e); - } catch (OpenshiftException e) { - throw new ProjectExistenceServiceException("Failed to check project in Openshift", e); - } catch (RuntimeException e) { - log.error("Unexpected error while checking project existence for key '{}'", projectKey, e); - throw new ProjectExistenceServiceException("Unexpected error while checking project existence", e); - } - } - - @Override - public boolean isProjectFoundByName(String projecName) throws ProjectExistenceServiceException { - return !projectService.findProjectsByName(projecName).isEmpty(); - } - - @Override - public boolean isProjectFoundInCollection(String projectKey) throws ProjectExistenceServiceException { - return existsInCollection(projectKey); - } - - private boolean existsInCollection(String projectKey) { - return projectService.getProject(projectKey) != null; - } - - private boolean existsInAnyBitbucketInstance(String projectKey) throws BitbucketException { - Set instances = bitbucketService.getAvailableInstances(); - for (String instance : instances.stream().sorted(Comparator.naturalOrder()).toList()) { - if (bitbucketService.projectExists(instance, projectKey)) return true; - } - return false; - } - - private boolean existsInAnyJiraInstance(String projectKey) throws JiraException { - Set instances = jiraService.getAvailableInstances(); - if (instances == null || instances.isEmpty()) { - return jiraService.projectExists(projectKey); - } - for (String instance : instances.stream().sorted(Comparator.naturalOrder()).toList()) { - if (jiraService.projectExists(instance, projectKey)) return true; - } - return false; - } - - private boolean existsInAnyOpenshift(String projectKey) throws OpenshiftException { - Set instances = openshiftService.getAvailableInstances(); - if (instances == null || instances.isEmpty()) return false; - for (String instance : instances.stream().sorted(Comparator.naturalOrder()).toList()) { - if (openshiftService.projectExists(instance, projectKey)) return true; - } - return false; - } -} +package org.opendevstack.apiservice.serviceproject.service.impl; + +import org.opendevstack.apiservice.externalservice.bitbucket.exception.BitbucketException; +import org.opendevstack.apiservice.externalservice.bitbucket.service.BitbucketService; +import org.opendevstack.apiservice.externalservice.jira.exception.JiraException; +import org.opendevstack.apiservice.externalservice.jira.service.JiraService; +import org.opendevstack.apiservice.externalservice.ocp.exception.OpenshiftException; +import org.opendevstack.apiservice.externalservice.ocp.service.OpenshiftService; +import org.opendevstack.apiservice.serviceproject.exception.ProjectExistenceServiceException; +import org.opendevstack.apiservice.serviceproject.service.ProjectExistenceService; +import org.opendevstack.apiservice.serviceproject.service.ProjectService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import lombok.extern.slf4j.Slf4j; + +import java.util.Comparator; +import java.util.Set; + +@Slf4j +@Service +public class ProjectExistenceServiceImpl implements ProjectExistenceService { + + private final BitbucketService bitbucketService; + private final JiraService jiraService; + private final OpenshiftService openshiftService; + private final ProjectService projectService; + + @Autowired + public ProjectExistenceServiceImpl(BitbucketService bitbucketService, JiraService jiraService, + OpenshiftService openshiftService, ProjectService projectService) { + this.bitbucketService = bitbucketService; + this.jiraService = jiraService; + this.openshiftService = openshiftService; + this.projectService = projectService; + } + @Override + public boolean isProjectFound(String projectKey) throws ProjectExistenceServiceException { + try { + if (existsInCollection(projectKey)) return true; + if (existsInAnyBitbucketInstance(projectKey)) return true; + if (existsInAnyJiraInstance(projectKey)) return true; + return existsInAnyOpenshift(projectKey); + } catch (BitbucketException e) { + throw new ProjectExistenceServiceException("Failed to check project in Bitbucket", e); + } catch (JiraException e) { + throw new ProjectExistenceServiceException("Failed to check project in Jira", e); + } catch (OpenshiftException e) { + throw new ProjectExistenceServiceException("Failed to check project in Openshift", e); + } catch (RuntimeException e) { + log.error("Unexpected error while checking project existence for key '{}'", projectKey, e); + throw new ProjectExistenceServiceException("Unexpected error while checking project existence", e); + } + } + + @Override + public boolean isProjectFoundByName(String projecName) throws ProjectExistenceServiceException { + return !projectService.findProjectsByName(projecName).isEmpty(); + } + + private boolean existsInCollection(String projectKey) { + return projectService.getProject(projectKey) != null; + } + + private boolean existsInAnyBitbucketInstance(String projectKey) throws BitbucketException { + Set instances = bitbucketService.getAvailableInstances(); + for (String instance : instances.stream().sorted(Comparator.naturalOrder()).toList()) { + if (bitbucketService.projectExists(instance, projectKey)) return true; + } + return false; + } + + private boolean existsInAnyJiraInstance(String projectKey) throws JiraException { + Set instances = jiraService.getAvailableInstances(); + if (instances == null || instances.isEmpty()) { + return jiraService.projectExists(projectKey); + } + for (String instance : instances.stream().sorted(Comparator.naturalOrder()).toList()) { + if (jiraService.projectExists(instance, projectKey)) return true; + } + return false; + } + + private boolean existsInAnyOpenshift(String projectKey) throws OpenshiftException { + Set instances = openshiftService.getAvailableInstances(); + if (instances == null || instances.isEmpty()) return false; + for (String instance : instances.stream().sorted(Comparator.naturalOrder()).toList()) { + if (openshiftService.projectExists(instance, projectKey)) return true; + } + return false; + } +} From cf995e1b607aeda57a6b5b72b882b1356e6b13c4 Mon Sep 17 00:00:00 2001 From: Joselee2908 Date: Wed, 13 May 2026 17:47:28 +0200 Subject: [PATCH 3/3] fix(api-project): fixed registration to only check collection for already existence --- .../impl/ProjectCreationCommandBuilder.java | 35 +++++++++- .../facade/impl/ProjectsFacadeImpl.java | 5 +- .../ProjectCreationCommandBuilderTest.java | 68 ++++++++++++++----- .../facade/impl/ProjectsFacadeImplTest.java | 10 +-- .../service/ProjectExistenceService.java | 2 + .../impl/ProjectExistenceServiceImpl.java | 5 ++ 6 files changed, 98 insertions(+), 27 deletions(-) diff --git a/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilder.java b/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilder.java index 15866ca..1597897 100644 --- a/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilder.java +++ b/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilder.java @@ -30,15 +30,26 @@ public class ProjectCreationCommandBuilder { private final ProjectExistenceService projectExistenceService; - public ProjectCreationCommand build(CreateProjectRequest request, ClientAppEntity clientApp) { + public ProjectCreationCommand buildForCreation(CreateProjectRequest request, ClientAppEntity clientApp) { ClientAppProjectFlavorEntity flavor = resolveFlavor(request, clientApp); + String projectKey = resolveProjectKey(request.getProjectKey(), flavor); + + return build(request, flavor, projectKey, clientApp); + } + + public ProjectCreationCommand buildForRegistration(CreateProjectRequest request, ClientAppEntity clientApp) { + ClientAppProjectFlavorEntity flavor = resolveFlavor(request, clientApp); + String projectKey = resolveProjectKeyForRegistration(request.getProjectKey(), flavor); + + return build(request, flavor, projectKey, clientApp); + } + private ProjectCreationCommand build(CreateProjectRequest request, ClientAppProjectFlavorEntity flavor, String projectKey, ClientAppEntity clientApp) { String projectFlavor = firstNonBlank(request.getProjectFlavor(), flavor.getName()); String configurationItem = firstNonBlank(request.getConfigurationItem(), flavor.getConfigItem()); String owner = firstNonBlank(request.getOwner(), flavor.getProjectOwner()); String x2account = firstNonBlank(request.getX2OdsAccount(), flavor.getServiceAccount()); String location = firstNonBlank(request.getLocation(), flavor.getLocation()); - String projectKey = resolveProjectKey(request.getProjectKey(), flavor); String projectName = resolveProjectName(request.getProjectName(), projectKey); String projectDescription = firstNonBlank(request.getProjectDescription(), "project " + projectFlavor); @@ -131,6 +142,26 @@ private String resolveProjectKey(String existingProjectKey, ClientAppProjectFlav } } + private String resolveProjectKeyForRegistration(String existingProjectKey, ClientAppProjectFlavorEntity flavor) { + try { + if (Strings.isNotEmpty(existingProjectKey)) { + if (!projectExistenceService.isProjectFoundInCollection(existingProjectKey)) { + return existingProjectKey; + } + + throw new ProjectAlreadyExistsException(ErrorKey.PROJECT_ALREADY_EXISTS); + } + + String pattern = flavor.getProjectKeyPattern(); + + return generateProjectKeyService.generateProjectKey(pattern); + } catch (ProjectKeyGenerationException e) { + throw new ProjectCreationException("Error generating the project key", e); + } catch (ProjectExistenceServiceException e) { + throw new ProjectCreationException("Error checking if the generated key exists: " + e.getMessage(), e); + } + } + private boolean isAllowedConfigItem(String configurationItem, ClientAppProjectFlavorEntity flavor) { String[] allowedConfigItems = flavor.getAllowedConfigItems(); diff --git a/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImpl.java b/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImpl.java index 746fcd4..e8df5f6 100644 --- a/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImpl.java +++ b/api-project/src/main/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImpl.java @@ -65,13 +65,14 @@ public CreateProjectResponse createProject(CreateProjectRequest request, UUID cl ClientAppEntity clientApp = clientAppService.findByClientId(clientId); - ProjectCreationCommand command = projectCreationCommandBuilder.build(request, clientApp); - + ProjectCreationCommand command; ProjectResponse project; if (Boolean.TRUE.equals(request.getRegistrationOnly())) { + command = projectCreationCommandBuilder.buildForRegistration(request, clientApp); project = registerProject(command); } else { + command = projectCreationCommandBuilder.buildForCreation(request, clientApp); project = createNewProject(command); } diff --git a/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilderTest.java b/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilderTest.java index 0c71c77..cef7943 100644 --- a/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilderTest.java +++ b/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectCreationCommandBuilderTest.java @@ -50,7 +50,7 @@ void tear_down() throws Exception { } @Test - void build_resolves_defaults_from_flavor_when_request_fields_are_missing() throws ProjectExistenceServiceException { + void build_for_creation_resolves_defaults_from_flavor_when_request_fields_are_missing() throws ProjectExistenceServiceException { ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); ClientAppEntity clientApp = build_client_app(List.of(flavor)); CreateProjectRequest request = build_request("DLSS", null, "KEY01"); @@ -59,7 +59,7 @@ void build_resolves_defaults_from_flavor_when_request_fields_are_missing() throw when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); - ProjectCreationCommand result = sut.build(request, clientApp); + ProjectCreationCommand result = sut.buildForCreation(request, clientApp); assertEquals("DLSS", result.getProjectFlavor()); assertEquals("CI-001", result.getConfigurationItem()); @@ -69,21 +69,21 @@ void build_resolves_defaults_from_flavor_when_request_fields_are_missing() throw } @Test - void build_resolves_flavor_from_configuration_item_when_flavor_is_not_provided() throws ProjectExistenceServiceException { + void build_for_creation_resolves_flavor_from_configuration_item_when_flavor_is_not_provided() throws ProjectExistenceServiceException { ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); ClientAppEntity clientApp = build_client_app(List.of(flavor)); CreateProjectRequest request = build_request(null, "CI-001", "KEY01"); when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); - ProjectCreationCommand result = sut.build(request, clientApp); + ProjectCreationCommand result = sut.buildForCreation(request, clientApp); assertEquals("DLSS", result.getProjectFlavor()); assertEquals("CI-001", result.getConfigurationItem()); } @Test - void build_generates_project_key_when_request_project_key_is_null() throws ProjectExistenceServiceException, org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException { + void build_for_creation_generates_project_key_when_request_project_key_is_null() throws ProjectExistenceServiceException, org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException { ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); ClientAppEntity clientApp = build_client_app(List.of(flavor)); CreateProjectRequest request = build_request("DLSS", null, null); @@ -91,14 +91,14 @@ void build_generates_project_key_when_request_project_key_is_null() throws Proje when(generateProjectKeyService.generateProjectKey("DLSS%06d")).thenReturn("DLSS000001"); when(projectExistenceService.isProjectFound("DLSS000001")).thenReturn(false); - ProjectCreationCommand result = sut.build(request, clientApp); + ProjectCreationCommand result = sut.buildForCreation(request, clientApp); assertEquals("DLSS000001", result.getProjectKey()); verify(generateProjectKeyService).generateProjectKey("DLSS%06d"); } @Test - void build_throws_project_already_exists_exception_when_project_key_already_exists() throws ProjectExistenceServiceException { + void build_for_creation_throws_project_already_exists_exception_when_project_key_already_exists() throws ProjectExistenceServiceException { ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); ClientAppEntity clientApp = build_client_app(List.of(flavor)); CreateProjectRequest request = build_request("DLSS", null, "KEY01"); @@ -106,35 +106,35 @@ void build_throws_project_already_exists_exception_when_project_key_already_exis when(projectExistenceService.isProjectFound("KEY01")).thenReturn(true); ProjectAlreadyExistsException ex = assertThrows(ProjectAlreadyExistsException.class, - () -> sut.build(request, clientApp)); + () -> sut.buildForCreation(request, clientApp)); assertEquals(ErrorKey.PROJECT_ALREADY_EXISTS, ex.getErrorKey()); } @Test - void build_throws_validation_exception_when_flavor_and_config_item_are_missing() { + void build_for_creation_throws_validation_exception_when_flavor_and_config_item_are_missing() { ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); ClientAppEntity clientApp = build_client_app(List.of(flavor)); CreateProjectRequest request = build_request(null, null, "KEY01"); ProjectValidationException ex = assertThrows(ProjectValidationException.class, - () -> sut.build(request, clientApp)); + () -> sut.buildForCreation(request, clientApp)); assertEquals(ErrorKey.BAD_REQUEST_FLAVOR_CONFIG_ITEM, ex.getErrorKey()); } @Test - void build_throws_validation_exception_when_configuration_item_matches_multiple_flavors() { + void build_for_creation_throws_validation_exception_when_configuration_item_matches_multiple_flavors() { ClientAppEntity clientApp = build_client_app(List.of( build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"), build_flavor("AMP", "CI-001", new String[] {}, "eu", "owner2"))); CreateProjectRequest request = build_request(null, "CI-001", "KEY01"); ProjectValidationException ex = assertThrows(ProjectValidationException.class, - () -> sut.build(request, clientApp)); + () -> sut.buildForCreation(request, clientApp)); assertEquals(ErrorKey.INVALID_CONFIG_ITEM, ex.getErrorKey()); } @Test - void build_throws_project_key_generation_exception_when_generation_fails() throws Exception { + void build_for_creation_throws_project_key_generation_exception_when_generation_fails() throws Exception { ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); ClientAppEntity clientApp = build_client_app(List.of(flavor)); CreateProjectRequest request = build_request("DLSS", null, null); @@ -142,11 +142,11 @@ void build_throws_project_key_generation_exception_when_generation_fails() throw when(generateProjectKeyService.generateProjectKey("DLSS%06d")) .thenThrow(new org.opendevstack.apiservice.serviceproject.exception.ProjectKeyGenerationException("fail")); - assertThrows(ProjectCreationException.class, () -> sut.build(request, clientApp)); + assertThrows(ProjectCreationException.class, () -> sut.buildForCreation(request, clientApp)); } @Test - void build_throws_project_already_exists_exception_when_project_name_already_exists() throws Exception { + void build_for_creation_throws_project_already_exists_exception_when_project_name_already_exists() throws Exception { ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); ClientAppEntity clientApp = build_client_app(List.of(flavor)); CreateProjectRequest request = build_request("DLSS", null, "KEY01"); @@ -155,11 +155,11 @@ void build_throws_project_already_exists_exception_when_project_name_already_exi when(projectExistenceService.isProjectFound("KEY01")).thenReturn(false); when(projectExistenceService.isProjectFoundByName("Existing Project")).thenReturn(true); - assertThrows(ProjectAlreadyExistsException.class, () -> sut.build(request, clientApp)); + assertThrows(ProjectAlreadyExistsException.class, () -> sut.buildForCreation(request, clientApp)); } @Test - void build_throws_project_creation_exception_when_project_name_check_fails() throws Exception { + void build_for_creation_throws_project_creation_exception_when_project_name_check_fails() throws Exception { ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); ClientAppEntity clientApp = build_client_app(List.of(flavor)); CreateProjectRequest request = build_request("DLSS", null, "KEY01"); @@ -169,7 +169,39 @@ void build_throws_project_creation_exception_when_project_name_check_fails() thr when(projectExistenceService.isProjectFoundByName("Any Project")) .thenThrow(new ProjectExistenceServiceException("lookup failed")); - assertThrows(ProjectCreationException.class, () -> sut.build(request, clientApp)); + assertThrows(ProjectCreationException.class, () -> sut.buildForCreation(request, clientApp)); + } + + @Test + void build_for_registration_resolves_defaults_from_flavor_when_request_fields_are_missing() throws ProjectExistenceServiceException { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, "KEY01"); + request.setOwner(null); + request.setLocation(null); + + when(projectExistenceService.isProjectFoundInCollection("KEY01")).thenReturn(false); + + ProjectCreationCommand result = sut.buildForRegistration(request, clientApp); + + assertEquals("DLSS", result.getProjectFlavor()); + assertEquals("CI-001", result.getConfigurationItem()); + assertEquals("owner1", result.getOwner()); + assertEquals("eu", result.getLocation()); + assertEquals("KEY01", result.getProjectKey()); + } + + @Test + void build_for_registration_throws_project_already_exists_exception_when_project_key_already_exists() throws ProjectExistenceServiceException { + ClientAppProjectFlavorEntity flavor = build_flavor("DLSS", "CI-001", new String[] {}, "eu", "owner1"); + ClientAppEntity clientApp = build_client_app(List.of(flavor)); + CreateProjectRequest request = build_request("DLSS", null, "KEY01"); + + when(projectExistenceService.isProjectFoundInCollection("KEY01")).thenReturn(true); + + ProjectAlreadyExistsException ex = assertThrows(ProjectAlreadyExistsException.class, + () -> sut.buildForRegistration(request, clientApp)); + assertEquals(ErrorKey.PROJECT_ALREADY_EXISTS, ex.getErrorKey()); } private CreateProjectRequest build_request(String flavor, String configItem, String projectKey) { diff --git a/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImplTest.java b/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImplTest.java index dd622ef..a81646c 100644 --- a/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImplTest.java +++ b/api-project/src/test/java/org/opendevstack/apiservice/project/facade/impl/ProjectsFacadeImplTest.java @@ -101,7 +101,7 @@ void create_project_returns_success_response_when_automation_is_successful() { apiResponse.setProjectFlavor("DLSS"); when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); - when(projectCreationCommandBuilder.build(request, clientApp)).thenReturn(command); + when(projectCreationCommandBuilder.buildForCreation(request, clientApp)).thenReturn(command); when(projectMapper.toServiceRequest(command)).thenReturn(serviceRequest); when(projectService.saveProject(serviceRequest)).thenReturn(projectResponse); when(automationParametersMapper.toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111")) @@ -114,7 +114,7 @@ void create_project_returns_success_response_when_automation_is_successful() { assertEquals("Pending", result.getStatus()); assertEquals("DLSS", result.getProjectFlavor()); - verify(projectCreationCommandBuilder).build(request, clientApp); + verify(projectCreationCommandBuilder).buildForCreation(request, clientApp); verify(projectMapper).toServiceRequest(command); verify(automationParametersMapper) .toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111"); @@ -139,7 +139,7 @@ void create_project_with_registrationOnly_returns_success_response_when_automati apiResponse.setProjectFlavor("REGULAR"); when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); - when(projectCreationCommandBuilder.build(request, clientApp)).thenReturn(command); + when(projectCreationCommandBuilder.buildForRegistration(request, clientApp)).thenReturn(command); when(projectMapper.toServiceRequest(command)).thenReturn(serviceRequest); when(projectService.saveProject(serviceRequest)).thenReturn(projectResponse); when(projectCreationResponseMapper.toSuccessResponse(command, projectResponse)).thenReturn(apiResponse); @@ -148,7 +148,7 @@ void create_project_with_registrationOnly_returns_success_response_when_automati assertEquals("Running", result.getStatus()); assertEquals("REGULAR", result.getProjectFlavor()); - verify(projectCreationCommandBuilder).build(request, clientApp); + verify(projectCreationCommandBuilder).buildForRegistration(request, clientApp); verify(projectMapper).toServiceRequest(command); verify(automationParametersMapper, never()) .toWorkflowParameters(command, "11111111-1111-1111-1111-111111111111"); @@ -164,7 +164,7 @@ void create_project_throws_project_creation_exception_when_automation_is_not_suc "DLSS01", "name", "desc", "DLSS", "CI-001", "eu", "x2test", "owner", CLIENT_ID); when(clientAppService.findByClientId(CLIENT_ID)).thenReturn(clientApp); - when(projectCreationCommandBuilder.build(request, clientApp)).thenReturn(command); + when(projectCreationCommandBuilder.buildForCreation(request, clientApp)).thenReturn(command); when(projectMapper.toServiceRequest(command)).thenReturn(new ProjectRequest()); when(projectService.saveProject(any(ProjectRequest.class))) .thenReturn(ProjectResponse.builder() diff --git a/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/ProjectExistenceService.java b/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/ProjectExistenceService.java index 3933043..7ab8dd5 100644 --- a/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/ProjectExistenceService.java +++ b/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/ProjectExistenceService.java @@ -7,4 +7,6 @@ public interface ProjectExistenceService { boolean isProjectFound(String projectKey) throws ProjectExistenceServiceException; boolean isProjectFoundByName(String projecName) throws ProjectExistenceServiceException; + + boolean isProjectFoundInCollection(String projectKey) throws ProjectExistenceServiceException; } diff --git a/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/impl/ProjectExistenceServiceImpl.java b/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/impl/ProjectExistenceServiceImpl.java index c576256..76e02e0 100644 --- a/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/impl/ProjectExistenceServiceImpl.java +++ b/service-projects/src/main/java/org/opendevstack/apiservice/serviceproject/service/impl/ProjectExistenceServiceImpl.java @@ -58,6 +58,11 @@ public boolean isProjectFoundByName(String projecName) throws ProjectExistenceSe return !projectService.findProjectsByName(projecName).isEmpty(); } + @Override + public boolean isProjectFoundInCollection(String projectKey) throws ProjectExistenceServiceException { + return existsInCollection(projectKey); + } + private boolean existsInCollection(String projectKey) { return projectService.getProject(projectKey) != null; }