diff --git a/src/main/java/org/opendevstack/component_provisioner/server/controllers/validators/MandatoryFieldsValidator.java b/src/main/java/org/opendevstack/component_provisioner/server/controllers/validators/MandatoryFieldsValidator.java index 9599105..d6fb839 100644 --- a/src/main/java/org/opendevstack/component_provisioner/server/controllers/validators/MandatoryFieldsValidator.java +++ b/src/main/java/org/opendevstack/component_provisioner/server/controllers/validators/MandatoryFieldsValidator.java @@ -1,6 +1,5 @@ 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; @@ -8,8 +7,6 @@ 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; @@ -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() diff --git a/src/main/java/org/opendevstack/component_provisioner/server/controllers/validators/ProvisionerActionsApiValidator.java b/src/main/java/org/opendevstack/component_provisioner/server/controllers/validators/ProvisionerActionsApiValidator.java index dbee44b..33d621a 100644 --- a/src/main/java/org/opendevstack/component_provisioner/server/controllers/validators/ProvisionerActionsApiValidator.java +++ b/src/main/java/org/opendevstack/component_provisioner/server/controllers/validators/ProvisionerActionsApiValidator.java @@ -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; @@ -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; @@ -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 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) { diff --git a/src/main/java/org/opendevstack/component_provisioner/server/facade/ProvisionerActionsApiFacade.java b/src/main/java/org/opendevstack/component_provisioner/server/facade/ProvisionerActionsApiFacade.java index d550c63..a2f8168 100644 --- a/src/main/java/org/opendevstack/component_provisioner/server/facade/ProvisionerActionsApiFacade.java +++ b/src/main/java/org/opendevstack/component_provisioner/server/facade/ProvisionerActionsApiFacade.java @@ -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); @@ -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); @@ -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() @@ -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, diff --git a/src/test/java/org/opendevstack/component_provisioner/server/controllers/validators/MandatoryFieldsValidatorTest.java b/src/test/java/org/opendevstack/component_provisioner/server/controllers/validators/MandatoryFieldsValidatorTest.java index c86f8c7..2e26be8 100644 --- a/src/test/java/org/opendevstack/component_provisioner/server/controllers/validators/MandatoryFieldsValidatorTest.java +++ b/src/test/java/org/opendevstack/component_provisioner/server/controllers/validators/MandatoryFieldsValidatorTest.java @@ -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; @@ -15,8 +14,6 @@ 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; @@ -24,18 +21,10 @@ 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; @@ -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"); } @@ -64,17 +48,12 @@ 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"); } @@ -82,8 +61,6 @@ void givenCatalogItemWithoutProvisionAction_whenValidate_thenExceptionIsThrown() @Test void givenMandatoryParameterWithEmptyList_whenValidate_thenExceptionIsThrown() { // given - var bearerToken = "bearer-token"; - CatalogItem catalogItem = CatalogItemMother.of(); CatalogItemUserActionParameter mandatoryParam = CatalogItemUserActionParameterMother.of( "mandatoryParam", @@ -91,15 +68,11 @@ void givenMandatoryParameterWithEmptyList_whenValidate_thenExceptionIsThrown() { ); 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"); } @@ -107,8 +80,6 @@ void givenMandatoryParameterWithEmptyList_whenValidate_thenExceptionIsThrown() { @Test void givenAValidProvisionAction_whenValidate_thenMandatoryFieldsAreProcessed() { // given - var bearerToken = "bearer-token"; - CatalogItem catalogItem = CatalogItemMother.of(); CatalogItemUserActionParameter mandatoryParam = CatalogItemUserActionParameterMother.of( "mandatoryParam", @@ -116,15 +87,11 @@ void givenAValidProvisionAction_whenValidate_thenMandatoryFieldsAreProcessed() { ); 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")); @@ -133,8 +100,6 @@ void givenAValidProvisionAction_whenValidate_thenMandatoryFieldsAreProcessed() { @Test void givenMandatoryParameterWithNullValue_whenValidate_thenExceptionIsThrown() { // given - var bearerToken = "bearer-token"; - CatalogItem catalogItem = CatalogItemMother.of(); CatalogItemUserActionParameter mandatoryParam = CatalogItemUserActionParameterMother.of( "mandatoryParam", @@ -142,15 +107,11 @@ void givenMandatoryParameterWithNullValue_whenValidate_thenExceptionIsThrown() { ); 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"); } @@ -158,8 +119,6 @@ void givenMandatoryParameterWithNullValue_whenValidate_thenExceptionIsThrown() { @Test void givenMandatoryParameterWithBlankStringValue_whenValidate_thenExceptionIsThrown() { // given - var bearerToken = "bearer-token"; - CatalogItem catalogItem = CatalogItemMother.of(); CatalogItemUserActionParameter mandatoryParam = CatalogItemUserActionParameterMother.of( "mandatoryParam", @@ -167,15 +126,11 @@ void givenMandatoryParameterWithBlankStringValue_whenValidate_thenExceptionIsThr ); 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"); } @@ -183,8 +138,6 @@ void givenMandatoryParameterWithBlankStringValue_whenValidate_thenExceptionIsThr @Test void givenMandatoryParameterWithNonBlankStringValue_whenValidate_thenValidationPasses() { // given - var bearerToken = "bearer-token"; - CatalogItem catalogItem = CatalogItemMother.of(); CatalogItemUserActionParameter mandatoryParam = CatalogItemUserActionParameterMother.of( "mandatoryParam", @@ -192,14 +145,11 @@ void givenMandatoryParameterWithNonBlankStringValue_whenValidate_thenValidationP ); 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); } } + diff --git a/src/test/java/org/opendevstack/component_provisioner/server/controllers/validators/ProvisionerActionsApiValidatorTest.java b/src/test/java/org/opendevstack/component_provisioner/server/controllers/validators/ProvisionerActionsApiValidatorTest.java index 4ff09e8..c338a93 100644 --- a/src/test/java/org/opendevstack/component_provisioner/server/controllers/validators/ProvisionerActionsApiValidatorTest.java +++ b/src/test/java/org/opendevstack/component_provisioner/server/controllers/validators/ProvisionerActionsApiValidatorTest.java @@ -8,6 +8,9 @@ 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.CatalogItemUserAction; +import org.opendevstack.component_provisioner.client.component_catalog.v1.model.CatalogItemUserActionParameter; import org.opendevstack.component_provisioner.client.component_catalog.v1.model.ProjectComponentInfo; import org.opendevstack.component_provisioner.config.ApplicationPropertiesConfiguration; import org.opendevstack.component_provisioner.server.services.AuthenticationProvider; @@ -23,8 +26,11 @@ import org.opendevstack.component_provisioner.server.services.restrictions.evaluators.GroupsRestrictionsEvaluator; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import static org.assertj.core.api.Assertions.assertThatNoException; +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.doThrow; @@ -106,7 +112,7 @@ void validate_throwsUserNotAllowedException_whenUserHasNoPermissions() { when(authenticationProvider.getAccessToken()).thenReturn(accessToken); - // Component catalog empty → no conflict + // Component catalog empty -> no conflict when(componentCatalogService.getProjectComponents(any(), any())) .thenReturn(List.of()); @@ -118,7 +124,7 @@ void validate_throwsUserNotAllowedException_whenUserHasNoPermissions() { when(catalogItemUserActionGroupsRestrictionProps.getPrefix()).thenReturn(List.of("prefix-")); when(catalogItemUserActionGroupsRestrictionProps.getSuffix()).thenReturn(List.of("-suffix")); - // Simulate evaluator result → forbidden + // Simulate evaluator result -> forbidden when(groupsRestrictionsEvaluator.evaluate(any(), any())) .thenReturn(Pair.of(false, "User is not allowed")); @@ -155,7 +161,7 @@ void validate_allowsProvision_whenUserHasPermissions() { when(catalogItemUserActionGroupsRestrictionProps.getPrefix()).thenReturn(List.of("prefix-")); when(catalogItemUserActionGroupsRestrictionProps.getSuffix()).thenReturn(List.of("-suffix")); - // Simulate evaluator result → allowed + // Simulate evaluator result -> allowed when(groupsRestrictionsEvaluator.evaluate(any(), any())) .thenReturn(Pair.of(true, "")); @@ -211,18 +217,12 @@ void validate_throwsInvalidRestEntityException_whenMandatoryFieldsValidatorThrow ProvisionActionParameterMother.of("access_token", "accessToken") )); - when(authenticationProvider.getAccessToken()).thenReturn("bearerToken"); - when(componentCatalogService.getProjectComponents(any(), any())).thenReturn(List.of()); - when(projectsInfoService.getProjectGroups(any())).thenReturn(List.of("group")); - when(catalogItemUserActionGroupsRestrictionProps.getPrefix()).thenReturn(List.of("prefix-")); - when(catalogItemUserActionGroupsRestrictionProps.getSuffix()).thenReturn(List.of("-suffix")); - when(groupsRestrictionsEvaluator.evaluate(any(), any())).thenReturn(Pair.of(true, "")); doThrow(new InvalidRestEntityException("Mandatory field missing")) .when(mandatoryFieldsValidator) - .validate(any()); + .validate(any(), any()); assertThrows(InvalidRestEntityException.class, - () -> provisionerActionsApiValidator.validate(action)); + () -> provisionerActionsApiValidator.validateMandatoryFields(action, new CatalogItem())); } @Test @@ -289,4 +289,178 @@ private ProvisionAction buildActionMissing(String missingParamName) { return ProvisionActionMother.of(params); } + + @Test + void validateReceivesOnlyVisibleParameters_throwsWhenCatalogItemIsNull() { + var action = ProvisionActionMother.of(Collections.emptyList()); + + assertThatThrownBy(() -> provisionerActionsApiValidator.validateReceivesOnlyVisibleParameters(action, null)) + .isInstanceOf(InvalidRestEntityException.class) + .hasMessageContaining("does not exist"); + } + + @Test + void validateReceivesOnlyVisibleParameters_throwsWhenCatalogItemHasNoUserActions() { + var catalogItem = CatalogItem.builder().title("My Item").userActions(null).build(); + var action = ProvisionActionMother.of(Collections.emptyList()); + + assertThatThrownBy(() -> provisionerActionsApiValidator.validateReceivesOnlyVisibleParameters(action, catalogItem)) + .isInstanceOf(InvalidRestEntityException.class) + .hasMessageContaining("does not exist"); + } + + @Test + void validateReceivesOnlyVisibleParameters_throwsWhenNoPROVISIONUserAction() { + var userAction = CatalogItemUserAction.builder().id("DELETE").parameters(List.of()).build(); + var catalogItem = CatalogItem.builder().title("My Item").userActions(List.of(userAction)).build(); + var action = ProvisionActionMother.of(Collections.emptyList()); + + assertThatThrownBy(() -> provisionerActionsApiValidator.validateReceivesOnlyVisibleParameters(action, catalogItem)) + .isInstanceOf(InvalidRestEntityException.class) + .hasMessageContaining("doesn't have a PROVISION user action"); + } + + @Test + void validateReceivesOnlyVisibleParameters_throwsWhenParameterIsNotDefinedInCatalog() { + var visibleParam = CatalogItemUserActionParameter.builder() + .name("known_param") + .visible(true) + .build(); + var userAction = CatalogItemUserAction.builder() + .id("PROVISION") + .parameters(List.of(visibleParam)) + .build(); + var catalogItem = CatalogItem.builder() + .title("My Catalog Item") + .userActions(List.of(userAction)) + .build(); + var action = ProvisionActionMother.of(List.of( + ProvisionActionParameterMother.of("unknown_param", "value") + )); + + assertThatThrownBy(() -> provisionerActionsApiValidator.validateReceivesOnlyVisibleParameters(action, catalogItem)) + .isInstanceOf(InvalidRestEntityException.class) + .hasMessageContaining("unknown_param") + .hasMessageContaining("My Catalog Item"); + } + + @Test + void validateReceivesOnlyVisibleParameters_throwsWhenParameterIsNotVisible() { + var hiddenParam = CatalogItemUserActionParameter.builder() + .name("hidden_param") + .visible(false) + .build(); + var userAction = CatalogItemUserAction.builder() + .id("PROVISION") + .parameters(List.of(hiddenParam)) + .build(); + var catalogItem = CatalogItem.builder() + .title("My Catalog Item") + .userActions(List.of(userAction)) + .build(); + var action = ProvisionActionMother.of(List.of( + ProvisionActionParameterMother.of("hidden_param", "value") + )); + + assertThatThrownBy(() -> provisionerActionsApiValidator.validateReceivesOnlyVisibleParameters(action, catalogItem)) + .isInstanceOf(InvalidRestEntityException.class) + .hasMessageContaining("hidden_param") + .hasMessageContaining("My Catalog Item"); + } + + @Test + void validateReceivesOnlyVisibleParameters_throwsWhenParameterVisibilityIsNull() { + var paramWithNullVisibility = CatalogItemUserActionParameter.builder() + .name("null_visibility_param") + .visible(null) + .build(); + var userAction = CatalogItemUserAction.builder() + .id("PROVISION") + .parameters(List.of(paramWithNullVisibility)) + .build(); + var catalogItem = CatalogItem.builder() + .title("My Catalog Item") + .userActions(List.of(userAction)) + .build(); + var action = ProvisionActionMother.of(List.of( + ProvisionActionParameterMother.of("null_visibility_param", "value") + )); + + assertThatThrownBy(() -> provisionerActionsApiValidator.validateReceivesOnlyVisibleParameters(action, catalogItem)) + .isInstanceOf(InvalidRestEntityException.class) + .hasMessageContaining("null_visibility_param") + .hasMessageContaining("My Catalog Item"); + } + + @Test + void validateReceivesOnlyVisibleParameters_succeedsWhenAllParametersAreVisible() { + var mandatoryVisible = CatalogItemUserActionParameter.builder() + .name("mandatory_param") + .visible(true) + .required(true) + .build(); + var optionalVisible = CatalogItemUserActionParameter.builder() + .name("optional_param") + .visible(true) + .required(false) + .build(); + var userAction = CatalogItemUserAction.builder() + .id("PROVISION") + .parameters(List.of(mandatoryVisible, optionalVisible)) + .build(); + var catalogItem = CatalogItem.builder() + .title("My Catalog Item") + .userActions(List.of(userAction)) + .build(); + var action = ProvisionActionMother.of(List.of( + ProvisionActionParameterMother.of("mandatory_param", "val1"), + ProvisionActionParameterMother.of("optional_param", "val2") + )); + + assertThatNoException().isThrownBy( + () -> provisionerActionsApiValidator.validateReceivesOnlyVisibleParameters(action, catalogItem)); + } + + @Test + void validateReceivesOnlyVisibleParameters_succeedsWhenOnlyVisibleOptionalParamProvided() { + var optionalVisible = CatalogItemUserActionParameter.builder() + .name("optional_param") + .visible(true) + .required(false) + .build(); + var userAction = CatalogItemUserAction.builder() + .id("PROVISION") + .parameters(List.of(optionalVisible)) + .build(); + var catalogItem = CatalogItem.builder() + .title("My Catalog Item") + .userActions(List.of(userAction)) + .build(); + var action = ProvisionActionMother.of(List.of( + ProvisionActionParameterMother.of("optional_param", "value") + )); + + assertThatNoException().isThrownBy( + () -> provisionerActionsApiValidator.validateReceivesOnlyVisibleParameters(action, catalogItem)); + } + + @Test + void validateReceivesOnlyVisibleParameters_succeedsWhenNoParametersProvided() { + var visibleParam = CatalogItemUserActionParameter.builder() + .name("some_param") + .visible(true) + .build(); + var userAction = CatalogItemUserAction.builder() + .id("PROVISION") + .parameters(List.of(visibleParam)) + .build(); + var catalogItem = CatalogItem.builder() + .title("My Catalog Item") + .userActions(List.of(userAction)) + .build(); + var action = ProvisionActionMother.of(Collections.emptyList()); + + assertThatNoException().isThrownBy( + () -> provisionerActionsApiValidator.validateReceivesOnlyVisibleParameters(action, catalogItem)); + } } diff --git a/src/test/java/org/opendevstack/component_provisioner/server/facade/ProvisionerActionsApiFacadeTest.java b/src/test/java/org/opendevstack/component_provisioner/server/facade/ProvisionerActionsApiFacadeTest.java index 24cba99..dcb02c5 100644 --- a/src/test/java/org/opendevstack/component_provisioner/server/facade/ProvisionerActionsApiFacadeTest.java +++ b/src/test/java/org/opendevstack/component_provisioner/server/facade/ProvisionerActionsApiFacadeTest.java @@ -94,7 +94,10 @@ void bypassAddMissingMandatoryParamsByDefault() { lenient() .doAnswer(invocation -> invocation.getArgument(0)) .when(facade) - .addMandatoryCatalogItemParamsIfMissing(any()); + .addMandatoryCatalogItemParamsIfMissing(any(), any()); + lenient() + .when(componentCatalogService.getCatalogItem(any(), any(), any())) + .thenReturn(new CatalogItem()); } @Test @@ -247,7 +250,6 @@ void addSystemParametersToAction_usesFirstCluster_whenMultipleClustersAreReturne void triggerProvisionAction_givenNoCatalogItemIdNorSlug_thenThrowsBadRequestException() { // given var action = ProvisionActionMother.of(List.of(ProvisionActionParameterMother.of("project_key", "PRJ"))); - setupSystemParameterMocks(); // when / then assertThatThrownBy(() -> facade.triggerProvisionAction(action)) @@ -264,7 +266,6 @@ void triggerProvisionAction_givenBothCatalogItemIdAndSlug_thenThrowsBadRequestEx ProvisionActionParameterMother.of("catalog_item_id", "cat-id"), ProvisionActionParameterMother.of("catalog_item_slug", "my-slug") )); - setupSystemParameterMocks(); // when / then assertThatThrownBy(() -> facade.triggerProvisionAction(action)) @@ -364,7 +365,7 @@ void triggerProvisionAction_givenOnlyCatalogItemSlug_thenAddMandatoryCatalogItem // then ArgumentCaptor wrapperCaptor = ArgumentCaptor.forClass(ProvisionActionWrapper.class); - verify(facade).addMandatoryCatalogItemParamsIfMissing(wrapperCaptor.capture()); + verify(facade).addMandatoryCatalogItemParamsIfMissing(wrapperCaptor.capture(), any()); var capturedWrapper = wrapperCaptor.getValue(); assertThat(capturedWrapper.getCatalogItemId()).isEqualTo(resolvedCatalogItemId); assertThat(capturedWrapper.getCatalogItemSlug()).isNull(); @@ -380,7 +381,7 @@ void triggerProvisionAction_givenCatalogItemSlugNotFound_thenThrowsSlugNotFoundE ProvisionActionParameterMother.of("project_key", "PRJ"), ProvisionActionParameterMother.of("catalog_item_slug", catalogItemSlug) )); - setupSystemParameterMocks(); + when(authenticationProvider.getAccessToken()).thenReturn(accessToken); when(componentCatalogService.getCatalogItemBySlug(accessToken, catalogItemSlug)) .thenThrow(new RestClientException("Not found")); @@ -456,7 +457,6 @@ void requestProvisionToAwx_addsActionIdParameter_whenParametersIsNull() { @Test void addMandatoryParamsIfMissing_addsMissingRequiredParameters() { // given - var accessToken = "ACCESS"; var actionId = "action-id"; var params = new ArrayList(); @@ -464,8 +464,6 @@ void addMandatoryParamsIfMissing_addsMissingRequiredParameters() { params.add(ProvisionActionParameterMother.of("project_key", "MY-PROJECT")); var action = ProvisionActionWrapperMother.of(params); - when(authenticationProvider.getAccessToken()).thenReturn(accessToken); - var requiredParam = new CatalogItemUserActionParameter() .name("required_param") .type("string") @@ -481,12 +479,10 @@ void addMandatoryParamsIfMissing_addsMissingRequiredParameters() { doCallRealMethod() .when(facade) - .addMandatoryCatalogItemParamsIfMissing(any()); - when(componentCatalogService.getCatalogItem(accessToken, "CAT-1", "MY-PROJECT")) - .thenReturn(catalogItem); + .addMandatoryCatalogItemParamsIfMissing(any(), any()); // when - var modifiedAction = facade.addMandatoryCatalogItemParamsIfMissing(action); + var modifiedAction = facade.addMandatoryCatalogItemParamsIfMissing(action, catalogItem); // then var addedParam = modifiedAction.getParametersMap().values().stream() @@ -501,7 +497,6 @@ void addMandatoryParamsIfMissing_addsMissingRequiredParameters() { @Test void addMandatoryParamsIfMissing_doesNothingWhenRequiredParamAlreadyPresent() { // given - var accessToken = "ACCESS"; var actionId = "ACTION_ID"; var params = new ArrayList(); @@ -510,8 +505,6 @@ void addMandatoryParamsIfMissing_doesNothingWhenRequiredParamAlreadyPresent() { params.add(ProvisionActionParameterMother.of("required_param", "custom")); var action = ProvisionActionWrapperMother.of(params); - when(authenticationProvider.getAccessToken()).thenReturn(accessToken); - var requiredParam = new CatalogItemUserActionParameter() .name("required_param") .type("string") @@ -527,12 +520,10 @@ void addMandatoryParamsIfMissing_doesNothingWhenRequiredParamAlreadyPresent() { doCallRealMethod() .when(facade) - .addMandatoryCatalogItemParamsIfMissing(any()); - when(componentCatalogService.getCatalogItem(accessToken, "CAT-1", "MY-PROJECT")) - .thenReturn(catalogItem); + .addMandatoryCatalogItemParamsIfMissing(any(), any()); // when - var modifiedAction = facade.addMandatoryCatalogItemParamsIfMissing(action); + var modifiedAction = facade.addMandatoryCatalogItemParamsIfMissing(action, catalogItem); // then var values = modifiedAction.getParametersMap().values().stream() @@ -545,15 +536,11 @@ void addMandatoryParamsIfMissing_doesNothingWhenRequiredParamAlreadyPresent() { @Test void addMandatoryParamsIfMissing_stringType_usesDefaultValue() { - var accessToken = "ACCESS"; - var action = ProvisionActionWrapperMother.of(List.of( ProvisionActionParameterMother.of("catalog_item_id", "CAT-1"), ProvisionActionParameterMother.of("project_key", "PRJ") )); - when(authenticationProvider.getAccessToken()).thenReturn(accessToken); - var requiredParam = CatalogItemUserActionParameter.builder() .name("param_string") .type("string") @@ -572,12 +559,9 @@ void addMandatoryParamsIfMissing_stringType_usesDefaultValue() { doCallRealMethod() .when(facade) - .addMandatoryCatalogItemParamsIfMissing(any()); - - when(componentCatalogService.getCatalogItem(accessToken, "CAT-1", "PRJ")) - .thenReturn(catalogItem); + .addMandatoryCatalogItemParamsIfMissing(any(), any()); - var result = facade.addMandatoryCatalogItemParamsIfMissing(action); + var result = facade.addMandatoryCatalogItemParamsIfMissing(action, catalogItem); var addedParam = result.getParametersMap().get("param_string"); assertThat(addedParam.getValue()).isEqualTo("default-value"); @@ -585,16 +569,12 @@ void addMandatoryParamsIfMissing_stringType_usesDefaultValue() { @Test void addMandatoryParamsIfMissing_stringType_usesLocationValue_whenNoDefault() { - var accessToken = "ACCESS"; - var action = ProvisionActionWrapperMother.of(List.of( ProvisionActionParameterMother.of("catalog_item_id", "CAT-1"), ProvisionActionParameterMother.of("project_key", "PRJ"), ProvisionActionParameterMother.of("cluster_location", "eu-west") )); - when(authenticationProvider.getAccessToken()).thenReturn(accessToken); - var location = CatalogItemUserActionParameterLocation.builder() .location("eu-west") .value("location-value") @@ -618,12 +598,9 @@ void addMandatoryParamsIfMissing_stringType_usesLocationValue_whenNoDefault() { doCallRealMethod() .when(facade) - .addMandatoryCatalogItemParamsIfMissing(any()); + .addMandatoryCatalogItemParamsIfMissing(any(), any()); - when(componentCatalogService.getCatalogItem(accessToken, "CAT-1", "PRJ")) - .thenReturn(catalogItem); - - var result = facade.addMandatoryCatalogItemParamsIfMissing(action); + var result = facade.addMandatoryCatalogItemParamsIfMissing(action, catalogItem); var addedParam = result.getParametersMap().get("param_string"); assertThat(addedParam.getValue()).isEqualTo("location-value"); @@ -631,15 +608,11 @@ void addMandatoryParamsIfMissing_stringType_usesLocationValue_whenNoDefault() { @Test void addMandatoryParamsIfMissing_stringType_withoutDefaults_leavesValueNull() { - var accessToken = "ACCESS"; - var action = ProvisionActionWrapperMother.of(List.of( ProvisionActionParameterMother.of("catalog_item_id", "CAT-1"), ProvisionActionParameterMother.of("project_key", "PRJ") )); - when(authenticationProvider.getAccessToken()).thenReturn(accessToken); - var requiredParam = CatalogItemUserActionParameter.builder() .name("param_string") .type("string") @@ -657,12 +630,9 @@ void addMandatoryParamsIfMissing_stringType_withoutDefaults_leavesValueNull() { doCallRealMethod() .when(facade) - .addMandatoryCatalogItemParamsIfMissing(any()); - - when(componentCatalogService.getCatalogItem(accessToken, "CAT-1", "PRJ")) - .thenReturn(catalogItem); + .addMandatoryCatalogItemParamsIfMissing(any(), any()); - var result = facade.addMandatoryCatalogItemParamsIfMissing(action); + var result = facade.addMandatoryCatalogItemParamsIfMissing(action, catalogItem); var addedParam = result.getParametersMap().get("param_string"); assertThat(addedParam.getValue()).isNull(); @@ -670,15 +640,11 @@ void addMandatoryParamsIfMissing_stringType_withoutDefaults_leavesValueNull() { @Test void addMandatoryParamsIfMissing_multipleListType_usesDefaultValues() { - var accessToken = "ACCESS"; - var action = ProvisionActionWrapperMother.of(List.of( ProvisionActionParameterMother.of("catalog_item_id", "CAT-1"), ProvisionActionParameterMother.of("project_key", "PRJ") )); - when(authenticationProvider.getAccessToken()).thenReturn(accessToken); - var requiredParam = CatalogItemUserActionParameter.builder() .name("param_multi") .type(MandatoryFieldType.MULTIPLELIST.getValue()) @@ -697,12 +663,9 @@ void addMandatoryParamsIfMissing_multipleListType_usesDefaultValues() { doCallRealMethod() .when(facade) - .addMandatoryCatalogItemParamsIfMissing(any()); - - when(componentCatalogService.getCatalogItem(accessToken, "CAT-1", "PRJ")) - .thenReturn(catalogItem); + .addMandatoryCatalogItemParamsIfMissing(any(), any()); - var result = facade.addMandatoryCatalogItemParamsIfMissing(action); + var result = facade.addMandatoryCatalogItemParamsIfMissing(action, catalogItem); var addedParam = result.getParametersMap().get("param_multi"); assertThat(addedParam.getValue()).isEqualTo(List.of("v1", "v2"));