Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,7 @@ api-project/src/main/java/org/opendevstack/apiservice/project/api
api-project/src/main/java/org/opendevstack/apiservice/project/model
api-project-component-v0/src/main/java/org/opendevstack/apiservice/project/api
api-project-component-v0/src/main/java/org/opendevstack/apiservice/project/model
external-service-marketplace/src/main/java/org/opendevstack/apiservice/externalservice/marketplace/openapi

**/.openapi-generator

Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ paths:
description: Component id
schema:
type: string
format: uuid
responses:
'200':
description: Component information
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ public static CreateComponentResponse forbidden(String path, String message, Com
return buildResponse(HttpStatus.FORBIDDEN, errorKey, path, message);
}

public static CreateComponentResponse conflict(String path, String message, ComponentErrorKey errorKey) {
return buildResponse(HttpStatus.CONFLICT, errorKey, path, message);
}

public static CreateComponentResponse notFound(String path, String message, ComponentErrorKey errorKey) {
return buildResponse(HttpStatus.NOT_FOUND, errorKey, path, message);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.opendevstack.apiservice.externalservice.marketplace.exception.MarketplaceException;
import org.opendevstack.apiservice.project.api.ProjectComponentsApi;
import org.opendevstack.apiservice.project.exception.ComponentCreationException;
import org.opendevstack.apiservice.project.exception.ComponentNotFoundException;
import org.opendevstack.apiservice.project.facade.ComponentsFacade;
import org.opendevstack.apiservice.project.mapper.ComponentResponseMapper;
Expand All @@ -15,8 +15,6 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
@AllArgsConstructor
@Slf4j
Expand All @@ -31,27 +29,34 @@

@Override
public ResponseEntity<CreateComponentResponse> createProjectComponent(String projectId, CreateComponentRequest createComponentRequest) {
Component component = componentsFacade.createProjectComponent(projectId, createComponentRequest);
if (component == null) {
throw new ComponentCreationException(String.format("Failed to create component for project '%s'", projectId));
}
componentsFacade.provisionProjectComponent(projectId, createComponentRequest);

log.info("Created component {} for project id {} and request {}", component, projectId, createComponentRequest);
log.info("Created component '{}' for project '{}'", createComponentRequest.getName(), projectId);
return componentResponseMapper.toResponseEntity(
ComponentsResponseFactory.entityCreated(projectId, component.getId())
ComponentsResponseFactory.entityCreated(projectId, createComponentRequest.getName())
);

}

@Override
public ResponseEntity<Component> getProjectComponent(String projectId, UUID componentId) {
Component component = componentsFacade.getProjectComponent(projectId, componentId.toString());
if (component == null) {
public ResponseEntity<Component> getProjectComponent(String projectId, String componentId) {
try {

Component component = componentsFacade.getProjectComponent(projectId, componentId);
if (component == null) {
throw new ComponentNotFoundException(
String.format("Component '%s' not found for project '%s'", componentId, projectId)
);
}

log.info("Retrieved component '{}' for project '{}': {}", componentId, projectId, component);
return ResponseEntity.status(HttpStatus.OK).body(component);
} catch (MarketplaceException e) { //TODO use error handler

Check warning on line 54 in api-project-component-v0/src/main/java/org/opendevstack/apiservice/project/controller/ProjectComponentsController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this TODO comment.

See more on https://sonarcloud.io/project/issues?id=opendevstack_ods-api-service&issues=AZ2vtBXMEKMbOcCSWJVg&open=AZ2vtBXMEKMbOcCSWJVg&pullRequest=18
log.error("Error while retrieving component '{}' for project '{}': {}", componentId, projectId, e.getMessage(), e);
throw new ComponentNotFoundException(
String.format("Component '%s' not found for project '%s'", componentId, projectId)
String.format("Failed to retrieve component '%s' for project '%s': %s", componentId, projectId, e.getMessage()), e
);
}

log.info("Retrieved component '{}' for project '{}': {}", componentId, projectId, component);
return ResponseEntity.status(HttpStatus.OK).body(component);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import lombok.extern.slf4j.Slf4j;
import org.opendevstack.apiservice.project.controller.ComponentsResponseFactory;
import org.opendevstack.apiservice.project.controller.ProjectComponentsController;
import org.opendevstack.apiservice.project.exception.ComponentAlreadyExistsException;
import org.opendevstack.apiservice.project.exception.ComponentCreationException;
import org.opendevstack.apiservice.project.exception.ComponentErrorKey;
import org.opendevstack.apiservice.project.exception.ComponentNotFoundException;
Expand Down Expand Up @@ -138,6 +139,22 @@ public ResponseEntity<CreateComponentResponse> handleComponentCreationException(
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}

@ExceptionHandler(ComponentAlreadyExistsException.class)
public ResponseEntity<CreateComponentResponse> handleComponentAlreadyExistsException(
ComponentAlreadyExistsException ex,
HttpServletRequest request) {

log.warn("Component already exists: {}", ex.getMessage());

CreateComponentResponse response = ComponentsResponseFactory.conflict(
request.getRequestURI(),
ex.getMessage(),
ComponentErrorKey.INVALID_PARAMETERS
);

return ResponseEntity.status(HttpStatus.CONFLICT).body(response);
}

@ExceptionHandler(Exception.class)
public ResponseEntity<CreateComponentResponse> handleGenericException(
Exception ex,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.opendevstack.apiservice.project.exception;

public class ComponentAlreadyExistsException extends RuntimeException {

public ComponentAlreadyExistsException(String message) {
super(message);
}

public ComponentAlreadyExistsException(String message, Exception e) {
super(message, e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ public class ComponentCreationException extends RuntimeException {
public ComponentCreationException(String message) {
super(message);
}

public ComponentCreationException(String message, Exception e) {
super(message, e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ public class ComponentNotFoundException extends RuntimeException {
public ComponentNotFoundException(String message) {
super(message);
}

public ComponentNotFoundException(String message, Exception e) {
super(message, e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.opendevstack.apiservice.externalservice.marketplace.model.CreateComponentParameter;
import org.opendevstack.apiservice.externalservice.marketplace.model.ProjectComponent;
import org.opendevstack.apiservice.externalservice.marketplace.exception.MarketplaceException;
import org.opendevstack.apiservice.externalservice.marketplace.openapi.model.ProjectComponentInfo;
import org.opendevstack.apiservice.externalservice.marketplace.openapi.model.ProvisionActionParameter;
import org.opendevstack.apiservice.externalservice.marketplace.service.MarketplaceService;
import org.opendevstack.apiservice.project.exception.ComponentAlreadyExistsException;
import org.opendevstack.apiservice.project.exception.ComponentCreationException;
import org.opendevstack.apiservice.project.exception.ComponentNotFoundException;
import org.opendevstack.apiservice.project.mapper.MarketplaceMapper;
import org.opendevstack.apiservice.project.model.Component;
import org.opendevstack.apiservice.project.model.CreateComponentRequest;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;

import java.util.List;

Expand All @@ -23,8 +26,8 @@ public class ComponentsFacade {

private final MarketplaceMapper marketplaceMapper;

public Component getProjectComponent(String projectId, String componentId) {
ProjectComponent marketplaceComponent = marketplaceExternalService.getProjectComponent(projectId, componentId);
public Component getProjectComponent(String projectId, String componentId) throws MarketplaceException {
ProjectComponentInfo marketplaceComponent = marketplaceExternalService.getProjectComponent(projectId, componentId);
if (marketplaceComponent == null) {
log.info("Marketplace component with id {} not found", componentId);
throw new ComponentNotFoundException(
Expand All @@ -34,15 +37,53 @@ public Component getProjectComponent(String projectId, String componentId) {
return marketplaceMapper.mapMarketplaceComponentToV0Component(marketplaceComponent);
}

public Component createProjectComponent(String projectId, CreateComponentRequest createComponentRequest) {
List<CreateComponentParameter> createComponentParameterList = marketplaceMapper.mapCreateComponentRequestToCreateComponentParameterList(createComponentRequest);
ProjectComponent marketplaceComponent = marketplaceExternalService.createProjectComponent(projectId, createComponentParameterList);
if (marketplaceComponent == null) {
log.error("Failed to create component in marketplace for project with id {}", projectId);
public void provisionProjectComponent(String projectId, CreateComponentRequest createComponentRequest) {
try {
List<ProvisionActionParameter> createComponentParameterList = marketplaceMapper.mapCreateComponentRequestToCreateComponentParameterList(createComponentRequest);
boolean success = marketplaceExternalService.provisionProjectComponent(projectId, createComponentParameterList);
if (!success) {
log.error("Failed to create component in marketplace for project with id {}", projectId);
throw new ComponentCreationException(
String.format("Failed to create component for project '%s'", projectId)
);
}
} catch (MarketplaceException e) {
if (isConflictCause(e)) {
throw new ComponentAlreadyExistsException(e.getMessage(), e);
}
throw new ComponentCreationException(
String.format("Failed to create component for project '%s'", projectId)
String.format("Failed to create component for project '%s': %s", projectId, e.getMessage()), e
);
}
return marketplaceMapper.mapMarketplaceComponentToV0Component(marketplaceComponent);
}

private boolean isConflictCause(Throwable throwable) {
Throwable current = throwable;
while (current != null) {
if (current instanceof HttpClientErrorException.Conflict) {
return true;
}
current = current.getCause();
}
return false;
}

public Boolean deleteProjectComponent(String projectId, String componentId) {
try {
return marketplaceExternalService.deleteProjectComponent(projectId, componentId);
} catch (MarketplaceException e) {
log.error("Failed to delete component with id {} for project with id {}", componentId, projectId, e);
return false;
}
}

public boolean registerProjectComponent(String projectId, String componentId) {
try {
marketplaceExternalService.registerProjectComponent(projectId, componentId);
return true;
} catch (MarketplaceException e) {
log.error("Failed to register component in marketplace for project with id {}", projectId, e);
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,70 +4,48 @@
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.opendevstack.apiservice.externalservice.marketplace.model.CreateComponentParameter;
import org.opendevstack.apiservice.externalservice.marketplace.model.ProjectComponent;
import org.opendevstack.apiservice.externalservice.marketplace.openapi.model.ProjectComponentInfo;
import org.opendevstack.apiservice.externalservice.marketplace.openapi.model.ProvisionActionParameter;
import org.opendevstack.apiservice.project.model.Component;
import org.opendevstack.apiservice.project.model.ComponentsStatusDTO;
import org.opendevstack.apiservice.project.model.CreateComponentRequest;
import org.opendevstack.apiservice.project.model.EnvironmentsDTO;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@Mapper(componentModel = "spring")
public interface MarketplaceMapper {

@Mapping(target = "id", source = "componentId", qualifiedByName = "uuidToString")
@Mapping(target = "environment", source = "environment", qualifiedByName = "toEnvironment")
@Mapping(target = "status", source = "status", qualifiedByName = "toComponentStatus")
@Mapping(target = "params", expression = "java(java.util.Collections.emptyMap())")
@Mapping(target = "id", source = "componentId")
@Mapping(target = "resultTraceback", ignore = true)
Component mapMarketplaceComponentToV0Component(ProjectComponent source);
Component mapMarketplaceComponentToV0Component(ProjectComponentInfo source);

default List<CreateComponentParameter> mapCreateComponentRequestToCreateComponentParameterList(CreateComponentRequest createComponentRequest) {
if (createComponentRequest == null || createComponentRequest.getParams() == null) {
default List<ProvisionActionParameter> mapCreateComponentRequestToCreateComponentParameterList(CreateComponentRequest createComponentRequest) {
if (createComponentRequest == null) {
return List.of();
}

return mapEntriesToCreateComponentParameterList(createComponentRequest.getParams().entrySet().stream().toList());
List<ProvisionActionParameter> parameters = new ArrayList<>();
parameters.add(createParameter("component_id", createComponentRequest.getName(), "string"));
parameters.add(createParameter("component_type", createComponentRequest.getProductId(), "string"));

if (createComponentRequest.getParams() != null && !createComponentRequest.getParams().isEmpty()) {
parameters.addAll(mapEntriesToCreateComponentParameterList(createComponentRequest.getParams().entrySet().stream().toList()));
}

return parameters;
}

default ProvisionActionParameter createParameter(String name, String value, String type) {
return new ProvisionActionParameter().name(name).type(type).value(value);
}

@IterableMapping(qualifiedByName = "toCreateComponentParameter")
List<CreateComponentParameter> mapEntriesToCreateComponentParameterList(List<Map.Entry<String, Object>> entries);
List<ProvisionActionParameter> mapEntriesToCreateComponentParameterList(List<Map.Entry<String, Object>> entries);

@Named("toCreateComponentParameter")
@Mapping(target = "name", source = "key")
@Mapping(target = "type", constant = "string")
@Mapping(target = "value", expression = "java(String.valueOf(entry.getValue()))")
CreateComponentParameter toCreateComponentParameter(Map.Entry<String, Object> entry);

@Named("uuidToString")
default String uuidToString(UUID sourceId) {
return sourceId != null ? sourceId.toString() : null;
}

@Named("toComponentStatus")
default ComponentsStatusDTO toComponentStatus(String sourceStatus) {
if (sourceStatus == null || sourceStatus.isBlank()) {
return null;
}
try {
return ComponentsStatusDTO.fromValue(sourceStatus);
} catch (IllegalArgumentException ex) {
return null;
}
}

@Named("toEnvironment")
default EnvironmentsDTO toEnvironment(String sourceEnv) {
if (sourceEnv == null || sourceEnv.isBlank()) {
return null;
}
try {
return EnvironmentsDTO.fromValue(sourceEnv);
} catch (IllegalArgumentException ex) {
return null;
}
}
ProvisionActionParameter toCreateComponentParameter(Map.Entry<String, Object> entry);
}
Loading
Loading