Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package org.opendevstack.component_provisioner.server.controllers.validators;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opendevstack.component_provisioner.client.component_catalog.v1.model.CatalogItem;
import org.opendevstack.component_provisioner.client.component_catalog.v1.model.CatalogItemUserActionParameter;
import org.opendevstack.component_provisioner.server.controllers.exceptions.InvalidRestEntityException;
import org.opendevstack.component_provisioner.server.model.ProvisionAction;
import org.opendevstack.component_provisioner.server.model.ProvisionActionParameter;
import org.opendevstack.component_provisioner.server.services.AuthenticationProvider;
import org.opendevstack.component_provisioner.server.services.ComponentCatalogService;
import org.springframework.stereotype.Service;

import java.util.Collections;
Expand All @@ -20,24 +17,13 @@
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.opendevstack.component_provisioner.server.services.ProvisionerActionsParameterExtractor.getCatalogItemId;
import static org.opendevstack.component_provisioner.server.services.ProvisionerActionsParameterExtractor.getProjectKey;
import static org.opendevstack.component_provisioner.server.services.ProvisionerActionsParameterExtractor.getLocation;

@Service
@AllArgsConstructor
@Slf4j
public class MandatoryFieldsValidator {

private final ComponentCatalogService componentCatalogService;
private final AuthenticationProvider authenticationProvider;

public void validate(ProvisionAction provisionAction) {
var projectKey = getProjectKey(provisionAction);
var catalogItemId = getCatalogItemId(provisionAction);
var accessToken = authenticationProvider.getAccessToken();

var catalogItem = componentCatalogService.getCatalogItem(accessToken, catalogItemId, projectKey);
public void validate(ProvisionAction provisionAction, CatalogItem catalogItem) {
var provisionUserAction = Optional.ofNullable(catalogItem)
.map(CatalogItem::getUserActions)
.map(userActions -> userActions.stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opendevstack.component_provisioner.client.component_catalog.v1.model.CatalogItem;
import org.opendevstack.component_provisioner.client.component_catalog.v1.model.CatalogItemUserActionParameter;
import org.opendevstack.component_provisioner.config.ApplicationPropertiesConfiguration;
import org.opendevstack.component_provisioner.server.controllers.exceptions.InvalidRestEntityException;
import org.opendevstack.component_provisioner.server.controllers.exceptions.ProjectComponentAlreadyProvisionedException;
import org.opendevstack.component_provisioner.server.controllers.exceptions.UserNotAllowedException;
import org.opendevstack.component_provisioner.server.controllers.model.ActionType;
import org.opendevstack.component_provisioner.server.model.ProvisionAction;
import org.opendevstack.component_provisioner.server.services.AuthenticationProvider;
import org.opendevstack.component_provisioner.server.services.ComponentCatalogService;
Expand All @@ -18,7 +21,13 @@
import org.opendevstack.component_provisioner.server.services.restrictions.evaluators.UserActionEntityRestrictions;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.opendevstack.component_provisioner.server.services.ProvisionerActionsParameterExtractor.getComponentId;
import static org.opendevstack.component_provisioner.server.services.ProvisionerActionsParameterExtractor.getProjectKey;
Expand Down Expand Up @@ -47,8 +56,35 @@ public void validate(ProvisionAction provisionAction) {
validateComponentIsNotProvisioned(projectKey, componentId);

validateUserHasPermissionsToProvision(projectKey, accessToken);
}

public void validateReceivesOnlyVisibleParameters(ProvisionAction provisionAction, CatalogItem catalogItem) {
var catalogItemProvisionUserAction = Optional.ofNullable(catalogItem)
.map(CatalogItem::getUserActions)
.map(userActions -> userActions.stream()
.filter(userAction -> ActionType.PROVISION.getValue().equals(userAction.getId()))
.findFirst()
.orElseThrow(() -> new InvalidRestEntityException("The catalog item doesn't have a PROVISION user action")))
.orElseThrow(() -> new InvalidRestEntityException("The catalog item does not exist, or doesn't have any user action"));

Map<String, CatalogItemUserActionParameter> catalogParamsByName = Optional.ofNullable(catalogItemProvisionUserAction.getParameters())
.orElse(Collections.emptyList())
.stream()
.collect(Collectors.toMap(CatalogItemUserActionParameter::getName, Function.identity()));

provisionAction.getParameters()
.forEach(param -> {
var catalogParam = catalogParamsByName.get(param.getName());
if (catalogParam == null || !Boolean.TRUE.equals(catalogParam.getVisible())) {
throw new InvalidRestEntityException(
String.format("The parameter '%s' is not allowed when provisioning '%s'.", param.getName(), catalogItem.getTitle())
);
}
});
}

mandatoryFieldsValidator.validate(provisionAction);
public void validateMandatoryFields(ProvisionAction provisionAction, CatalogItem catalogItem) {
mandatoryFieldsValidator.validate(provisionAction, catalogItem);
}

private void validateUserHasPermissionsToProvision(String projectKey, String accessToken) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@ public class ProvisionerActionsApiFacade {

public AwxResponse triggerProvisionAction(ProvisionAction provisionAction) {
log.info("Triggering provisioner action with id: '{}'", provisionAction.getId());

provisionerActionsApiValidator.validate(provisionAction);
var provisionActionWrapper = new ProvisionActionWrapper(provisionAction);
var systemParametersActionWrapper = addSystemParametersToAction(provisionActionWrapper);
var resolvedActionWrapper = resolveCatalogItemIdentifier(systemParametersActionWrapper);
var requiredCatalogItemParamsWrapper = addMandatoryCatalogItemParamsIfMissing(resolvedActionWrapper);
provisionerActionsApiValidator.validate(requiredCatalogItemParamsWrapper.toProvisionAction());
var resolvedActionWrapper = resolveCatalogItemIdentifier(provisionActionWrapper);
var catalogItem = fetchCatalogItem(resolvedActionWrapper);
provisionerActionsApiValidator.validateReceivesOnlyVisibleParameters(resolvedActionWrapper.toProvisionAction(), catalogItem);
var systemParametersActionWrapper = addSystemParametersToAction(resolvedActionWrapper);
var requiredCatalogItemParamsWrapper = addMandatoryCatalogItemParamsIfMissing(systemParametersActionWrapper, catalogItem);
provisionerActionsApiValidator.validateMandatoryFields(requiredCatalogItemParamsWrapper.toProvisionAction(), catalogItem);
var updateProvisionActionWithoutPlaceholdersWrapper = placeholderPostProcessor.process(requiredCatalogItemParamsWrapper);
var updatedProvisionActionWithOdsApiParametersWrapper = replaceParametersService.replaceProvisioningParametersFromOdsApi(updateProvisionActionWithoutPlaceholdersWrapper);

Expand Down Expand Up @@ -220,7 +222,7 @@ private ProvisionActionWrapper resolveCatalogItemIdentifier(ProvisionActionWrapp

// Only catalog_item_slug provided: resolve to catalog_item_id
log.debug("Resolving catalog_item_id for catalog_item_slug: {}", catalogItemSlug);
var accessToken = wrapper.getAccessToken();
var accessToken = authenticationProvider.getAccessToken();
CatalogItem catalogItem;
try {
catalogItem = componentCatalogService.getCatalogItemBySlug(accessToken, catalogItemSlug);
Expand Down Expand Up @@ -256,13 +258,7 @@ private ProvisionAction addParametersItem(ProvisionAction provisionAction, Provi
.orElse(provisionAction);
}

public ProvisionActionWrapper addMandatoryCatalogItemParamsIfMissing(ProvisionActionWrapper provisionActionWrapper) {
var accessToken = authenticationProvider.getAccessToken();
var catalogItemId = provisionActionWrapper.getCatalogItemId();
var projectKey = provisionActionWrapper.getProjectKey();

CatalogItem catalogItem = componentCatalogService.getCatalogItem(accessToken, catalogItemId, projectKey);

public ProvisionActionWrapper addMandatoryCatalogItemParamsIfMissing(ProvisionActionWrapper provisionActionWrapper, CatalogItem catalogItem) {
var mandatoryParams = Optional.ofNullable(catalogItem.getUserActions())
.orElse(List.of())
.stream()
Expand Down Expand Up @@ -294,6 +290,11 @@ public ProvisionActionWrapper addMandatoryCatalogItemParamsIfMissing(ProvisionAc
return res;
}

private CatalogItem fetchCatalogItem(ProvisionActionWrapper wrapper) {
var accessToken = authenticationProvider.getAccessToken();
return componentCatalogService.getCatalogItem(accessToken, wrapper.getCatalogItemId(), wrapper.getProjectKey());
}

private void applyDefaultValue(
ProvisionActionParameter param,
CatalogItemUserActionParameter catalogParam,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.opendevstack.component_provisioner.client.component_catalog.v1.model.CatalogItem;
import org.opendevstack.component_provisioner.client.component_catalog.v1.model.CatalogItemUserActionParameter;
Expand All @@ -15,27 +14,17 @@
import org.opendevstack.component_provisioner.server.model.ProvisionActionMother;
import org.opendevstack.component_provisioner.server.model.ProvisionActionParameter;
import org.opendevstack.component_provisioner.server.model.ProvisionActionParameterMother;
import org.opendevstack.component_provisioner.server.services.AuthenticationProvider;
import org.opendevstack.component_provisioner.server.services.ComponentCatalogService;

import java.util.Collections;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class MandatoryFieldsValidatorTest {

@Mock
private ComponentCatalogService componentCatalogService;

@Mock
private AuthenticationProvider authenticationProvider;

@InjectMocks
private MandatoryFieldsValidator validator;

Expand All @@ -44,17 +33,12 @@ void givenCatalogItemWithoutUserActions_whenValidate_thenExceptionIsThrown() {
// given
CatalogItem catalogItem = CatalogItemMother.of();
catalogItem.setUserActions(null);
var bearerToken = "bearer-token";

when(authenticationProvider.getAccessToken()).thenReturn(bearerToken);
when(componentCatalogService.getCatalogItem(any(), any(), any()))
.thenReturn(catalogItem);

ProvisionAction action =
ProvisionActionMother.of(Collections.emptyList());

// when / then
assertThatThrownBy(() -> validator.validate(action))
assertThatThrownBy(() -> validator.validate(action, catalogItem))
.isInstanceOf(InvalidRestEntityException.class)
.hasMessageContaining("does not exist");
}
Expand All @@ -64,67 +48,50 @@ void givenCatalogItemWithoutProvisionAction_whenValidate_thenExceptionIsThrown()
// given
CatalogItem catalogItem = CatalogItemMother.of();
catalogItem.getUserActions().getFirst().setId("DELETE");
var bearerToken = "bearer-token";

when(authenticationProvider.getAccessToken()).thenReturn(bearerToken);
when(componentCatalogService.getCatalogItem(any(), any(), any()))
.thenReturn(catalogItem);

ProvisionAction action =
ProvisionActionMother.of(Collections.emptyList());

// when / then
assertThatThrownBy(() -> validator.validate(action))
assertThatThrownBy(() -> validator.validate(action, catalogItem))
.isInstanceOf(InvalidRestEntityException.class)
.hasMessageContaining("doesn't have a PROVISION user action");
}

@Test
void givenMandatoryParameterWithEmptyList_whenValidate_thenExceptionIsThrown() {
// given
var bearerToken = "bearer-token";

CatalogItem catalogItem = CatalogItemMother.of();
CatalogItemUserActionParameter mandatoryParam = CatalogItemUserActionParameterMother.of(
"mandatoryParam",
"defaultValue"
);
catalogItem.getUserActions().getFirst().setParameters(List.of(mandatoryParam));

when(authenticationProvider.getAccessToken()).thenReturn(bearerToken);
when(componentCatalogService.getCatalogItem(any(), any(), any()))
.thenReturn(catalogItem);

ProvisionActionParameter actionParam = ProvisionActionParameterMother.of("mandatoryParam", Collections.emptyList());
ProvisionAction action = ProvisionActionMother.of(List.of(actionParam));

// when / then
assertThatThrownBy(() -> validator.validate(action))
assertThatThrownBy(() -> validator.validate(action, catalogItem))
.isInstanceOf(InvalidRestEntityException.class)
.hasMessageContaining("mandatory and no value was provided");
}

@Test
void givenAValidProvisionAction_whenValidate_thenMandatoryFieldsAreProcessed() {
// given
var bearerToken = "bearer-token";

CatalogItem catalogItem = CatalogItemMother.of();
CatalogItemUserActionParameter mandatoryParam = CatalogItemUserActionParameterMother.of(
"mandatoryParam",
"defaultValue"
);
catalogItem.getUserActions().getFirst().setParameters(List.of(mandatoryParam));

when(authenticationProvider.getAccessToken()).thenReturn(bearerToken);
when(componentCatalogService.getCatalogItem(any(), any(), any()))
.thenReturn(catalogItem);

ProvisionActionParameter actionParam = ProvisionActionParameterMother.of("mandatoryParam", List.of("defaultValue"));
ProvisionAction action = ProvisionActionMother.of(List.of(actionParam));

// when
validator.validate(action);
validator.validate(action, catalogItem);

// then
assertThat(actionParam.getValue()).isEqualTo(List.of("defaultValue"));
Expand All @@ -133,73 +100,56 @@ void givenAValidProvisionAction_whenValidate_thenMandatoryFieldsAreProcessed() {
@Test
void givenMandatoryParameterWithNullValue_whenValidate_thenExceptionIsThrown() {
// given
var bearerToken = "bearer-token";

CatalogItem catalogItem = CatalogItemMother.of();
CatalogItemUserActionParameter mandatoryParam = CatalogItemUserActionParameterMother.of(
"mandatoryParam",
"defaultValue"
);
catalogItem.getUserActions().getFirst().setParameters(List.of(mandatoryParam));

when(authenticationProvider.getAccessToken()).thenReturn(bearerToken);
when(componentCatalogService.getCatalogItem(any(), any(), any()))
.thenReturn(catalogItem);

ProvisionActionParameter actionParam = ProvisionActionParameterMother.of("mandatoryParam", (Object) null);
ProvisionAction action = ProvisionActionMother.of(List.of(actionParam));

// when / then
assertThatThrownBy(() -> validator.validate(action))
assertThatThrownBy(() -> validator.validate(action, catalogItem))
.isInstanceOf(InvalidRestEntityException.class)
.hasMessageContaining("mandatory and no value was provided");
}

@Test
void givenMandatoryParameterWithBlankStringValue_whenValidate_thenExceptionIsThrown() {
// given
var bearerToken = "bearer-token";

CatalogItem catalogItem = CatalogItemMother.of();
CatalogItemUserActionParameter mandatoryParam = CatalogItemUserActionParameterMother.of(
"mandatoryParam",
"defaultValue"
);
catalogItem.getUserActions().getFirst().setParameters(List.of(mandatoryParam));

when(authenticationProvider.getAccessToken()).thenReturn(bearerToken);
when(componentCatalogService.getCatalogItem(any(), any(), any()))
.thenReturn(catalogItem);

ProvisionActionParameter actionParam = ProvisionActionParameterMother.of("mandatoryParam", " ");
ProvisionAction action = ProvisionActionMother.of(List.of(actionParam));

// when / then
assertThatThrownBy(() -> validator.validate(action))
assertThatThrownBy(() -> validator.validate(action, catalogItem))
.isInstanceOf(InvalidRestEntityException.class)
.hasMessageContaining("mandatory and no value was provided");
}

@Test
void givenMandatoryParameterWithNonBlankStringValue_whenValidate_thenValidationPasses() {
// given
var bearerToken = "bearer-token";

CatalogItem catalogItem = CatalogItemMother.of();
CatalogItemUserActionParameter mandatoryParam = CatalogItemUserActionParameterMother.of(
"mandatoryParam",
"defaultValue"
);
catalogItem.getUserActions().getFirst().setParameters(List.of(mandatoryParam));

when(authenticationProvider.getAccessToken()).thenReturn(bearerToken);
when(componentCatalogService.getCatalogItem(any(), any(), any()))
.thenReturn(catalogItem);

ProvisionActionParameter actionParam = ProvisionActionParameterMother.of("mandatoryParam", "someValue");
ProvisionAction action = ProvisionActionMother.of(List.of(actionParam));

// when / then (no exception)
validator.validate(action);
validator.validate(action, catalogItem);
}
}

Loading
Loading