From 1b9c2f7e4b183844ce6c970039012f306ad93ca2 Mon Sep 17 00:00:00 2001 From: "Vila,Jordi (IT EDP)" Date: Tue, 12 May 2026 08:03:41 +0200 Subject: [PATCH 1/3] Refactor triggerProvisionAction to only fetch catalog item once --- .../validators/MandatoryFieldsValidator.java | 16 +---- .../ProvisionerActionsApiValidator.java | 5 +- .../facade/ProvisionerActionsApiFacade.java | 18 ++--- .../MandatoryFieldsValidatorTest.java | 66 +++--------------- .../ProvisionerActionsApiValidatorTest.java | 31 +++++---- .../ProvisionerActionsApiFacadeTest.java | 69 +++++-------------- 6 files changed, 54 insertions(+), 151 deletions(-) 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 95991057..d6fb8390 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 dbee44bb..9ebe11e7 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,6 +3,7 @@ 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.config.ApplicationPropertiesConfiguration; import org.opendevstack.component_provisioner.server.controllers.exceptions.InvalidRestEntityException; import org.opendevstack.component_provisioner.server.controllers.exceptions.ProjectComponentAlreadyProvisionedException; @@ -35,7 +36,7 @@ public class ProvisionerActionsApiValidator { private final ProjectsInfoService projectsInfoService; private final MandatoryFieldsValidator mandatoryFieldsValidator; - public void validate(ProvisionAction provisionAction) { + public void validate(ProvisionAction provisionAction, CatalogItem catalogItem) { log.debug("Start validation for provisionActions: {}", provisionAction); var projectKey = getProjectKey(provisionAction); @@ -48,7 +49,7 @@ public void validate(ProvisionAction provisionAction) { validateUserHasPermissionsToProvision(projectKey, accessToken); - mandatoryFieldsValidator.validate(provisionAction); + 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 d550c63e..ff3d2371 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 @@ -49,8 +49,9 @@ public AwxResponse triggerProvisionAction(ProvisionAction provisionAction) { var provisionActionWrapper = new ProvisionActionWrapper(provisionAction); var systemParametersActionWrapper = addSystemParametersToAction(provisionActionWrapper); var resolvedActionWrapper = resolveCatalogItemIdentifier(systemParametersActionWrapper); - var requiredCatalogItemParamsWrapper = addMandatoryCatalogItemParamsIfMissing(resolvedActionWrapper); - provisionerActionsApiValidator.validate(requiredCatalogItemParamsWrapper.toProvisionAction()); + var catalogItem = fetchCatalogItem(resolvedActionWrapper); + var requiredCatalogItemParamsWrapper = addMandatoryCatalogItemParamsIfMissing(resolvedActionWrapper, catalogItem); + provisionerActionsApiValidator.validate(requiredCatalogItemParamsWrapper.toProvisionAction(), catalogItem); var updateProvisionActionWithoutPlaceholdersWrapper = placeholderPostProcessor.process(requiredCatalogItemParamsWrapper); var updatedProvisionActionWithOdsApiParametersWrapper = replaceParametersService.replaceProvisioningParametersFromOdsApi(updateProvisionActionWithoutPlaceholdersWrapper); @@ -256,13 +257,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 +289,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 c86f8c7c..2e26be89 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 4ff09e83..20b05214 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,7 @@ 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.ProjectComponentInfo; import org.opendevstack.component_provisioner.config.ApplicationPropertiesConfiguration; import org.opendevstack.component_provisioner.server.services.AuthenticationProvider; @@ -60,7 +61,7 @@ void validate_throwsInvalidRestEntityException_whenRequiredParameterMissing(Stri var action = buildActionMissing(missingParam); assertThrows(InvalidRestEntityException.class, - () -> provisionerActionsApiValidator.validate(action)); + () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); } @Test @@ -87,7 +88,7 @@ void validate_throwsProjectComponentAlreadyProvisionedException_whenComponentAlr .thenReturn(List.of(exists)); assertThrows(ProjectComponentAlreadyProvisionedException.class, - () -> provisionerActionsApiValidator.validate(action)); + () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); } @Test @@ -106,7 +107,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,13 +119,13 @@ 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")); // Expect exception assertThrows(UserNotAllowedException.class, - () -> provisionerActionsApiValidator.validate(action)); + () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); } @Test @@ -155,12 +156,12 @@ 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, "")); // Should NOT throw - provisionerActionsApiValidator.validate(action); + provisionerActionsApiValidator.validate(action, new CatalogItem()); } @Test @@ -173,7 +174,7 @@ void validate_throwsInvalidRestEntityException_whenProjectKeyIsBlank() { )); assertThrows(InvalidRestEntityException.class, - () -> provisionerActionsApiValidator.validate(action)); + () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); } @Test @@ -186,7 +187,7 @@ void validate_throwsInvalidRestEntityException_whenComponentIdIsBlank() { )); assertThrows(InvalidRestEntityException.class, - () -> provisionerActionsApiValidator.validate(action)); + () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); } @Test @@ -199,7 +200,7 @@ void validate_throwsInvalidRestEntityException_whenAccessTokenIsBlank() { )); assertThrows(InvalidRestEntityException.class, - () -> provisionerActionsApiValidator.validate(action)); + () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); } @Test @@ -219,10 +220,10 @@ void validate_throwsInvalidRestEntityException_whenMandatoryFieldsValidatorThrow 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.validate(action, new CatalogItem())); } @Test @@ -238,7 +239,7 @@ void validate_throwsException_whenComponentCatalogServiceThrowsDuringProvisionCh when(componentCatalogService.getProjectComponents(any(), any())).thenThrow(new RuntimeException("Service error")); assertThrows(RuntimeException.class, - () -> provisionerActionsApiValidator.validate(action)); + () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); } @Test @@ -255,7 +256,7 @@ void validate_throwsException_whenProjectsInfoServiceThrowsDuringPermissionsChec when(projectsInfoService.getProjectGroups(any())).thenThrow(new RuntimeException("Service error")); assertThrows(RuntimeException.class, - () -> provisionerActionsApiValidator.validate(action)); + () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); } @Test @@ -273,7 +274,7 @@ void validate_throwsException_whenGroupsRestrictionsEvaluatorThrowsDuringPermiss when(groupsRestrictionsEvaluator.evaluate(any(), any())).thenThrow(new RuntimeException("Evaluator error")); assertThrows(RuntimeException.class, - () -> provisionerActionsApiValidator.validate(action)); + () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); } private ProvisionAction buildActionMissing(String missingParamName) { 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 24cba990..cb79c2cb 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 @@ -364,7 +367,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(); @@ -456,7 +459,6 @@ void requestProvisionToAwx_addsActionIdParameter_whenParametersIsNull() { @Test void addMandatoryParamsIfMissing_addsMissingRequiredParameters() { // given - var accessToken = "ACCESS"; var actionId = "action-id"; var params = new ArrayList(); @@ -464,8 +466,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 +481,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 +499,6 @@ void addMandatoryParamsIfMissing_addsMissingRequiredParameters() { @Test void addMandatoryParamsIfMissing_doesNothingWhenRequiredParamAlreadyPresent() { // given - var accessToken = "ACCESS"; var actionId = "ACTION_ID"; var params = new ArrayList(); @@ -510,8 +507,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 +522,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 +538,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 +561,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 +571,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 +600,9 @@ void addMandatoryParamsIfMissing_stringType_usesLocationValue_whenNoDefault() { 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("location-value"); @@ -631,15 +610,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 +632,9 @@ void addMandatoryParamsIfMissing_stringType_withoutDefaults_leavesValueNull() { 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()).isNull(); @@ -670,15 +642,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 +665,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")); From 848f5c8e04b114c79c5b1f88b423d4b9b9275022 Mon Sep 17 00:00:00 2001 From: "Vila,Jordi (IT EDP)" Date: Tue, 12 May 2026 08:10:11 +0200 Subject: [PATCH 2/3] Change method call order --- .../server/facade/ProvisionerActionsApiFacade.java | 8 ++++---- .../server/facade/ProvisionerActionsApiFacadeTest.java | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) 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 ff3d2371..b6e418d4 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 @@ -47,10 +47,10 @@ public AwxResponse triggerProvisionAction(ProvisionAction provisionAction) { log.info("Triggering provisioner action with id: '{}'", provisionAction.getId()); var provisionActionWrapper = new ProvisionActionWrapper(provisionAction); - var systemParametersActionWrapper = addSystemParametersToAction(provisionActionWrapper); - var resolvedActionWrapper = resolveCatalogItemIdentifier(systemParametersActionWrapper); + var resolvedActionWrapper = resolveCatalogItemIdentifier(provisionActionWrapper); var catalogItem = fetchCatalogItem(resolvedActionWrapper); - var requiredCatalogItemParamsWrapper = addMandatoryCatalogItemParamsIfMissing(resolvedActionWrapper, catalogItem); + var systemParametersActionWrapper = addSystemParametersToAction(resolvedActionWrapper); + var requiredCatalogItemParamsWrapper = addMandatoryCatalogItemParamsIfMissing(systemParametersActionWrapper, catalogItem); provisionerActionsApiValidator.validate(requiredCatalogItemParamsWrapper.toProvisionAction(), catalogItem); var updateProvisionActionWithoutPlaceholdersWrapper = placeholderPostProcessor.process(requiredCatalogItemParamsWrapper); var updatedProvisionActionWithOdsApiParametersWrapper = replaceParametersService.replaceProvisioningParametersFromOdsApi(updateProvisionActionWithoutPlaceholdersWrapper); @@ -221,7 +221,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); 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 cb79c2cb..dcb02c57 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 @@ -250,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)) @@ -267,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)) @@ -383,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")); From c372ed1589dcdb269a6d90108bc63819a173dd18 Mon Sep 17 00:00:00 2001 From: "Vila,Jordi (IT EDP)" Date: Tue, 12 May 2026 09:53:48 +0200 Subject: [PATCH 3/3] Add validateReceivesOnlyVisibleParameters method --- .../ProvisionerActionsApiValidator.java | 37 +++- .../facade/ProvisionerActionsApiFacade.java | 5 +- .../ProvisionerActionsApiValidatorTest.java | 207 ++++++++++++++++-- 3 files changed, 229 insertions(+), 20 deletions(-) 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 9ebe11e7..33d621a7 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 @@ -4,10 +4,12 @@ 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; @@ -19,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; @@ -36,7 +44,7 @@ public class ProvisionerActionsApiValidator { private final ProjectsInfoService projectsInfoService; private final MandatoryFieldsValidator mandatoryFieldsValidator; - public void validate(ProvisionAction provisionAction, CatalogItem catalogItem) { + public void validate(ProvisionAction provisionAction) { log.debug("Start validation for provisionActions: {}", provisionAction); var projectKey = getProjectKey(provisionAction); @@ -48,7 +56,34 @@ public void validate(ProvisionAction provisionAction, CatalogItem catalogItem) { 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()) + ); + } + }); + } + public void validateMandatoryFields(ProvisionAction provisionAction, CatalogItem catalogItem) { mandatoryFieldsValidator.validate(provisionAction, catalogItem); } 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 b6e418d4..a2f81687 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,13 +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 resolvedActionWrapper = resolveCatalogItemIdentifier(provisionActionWrapper); var catalogItem = fetchCatalogItem(resolvedActionWrapper); + provisionerActionsApiValidator.validateReceivesOnlyVisibleParameters(resolvedActionWrapper.toProvisionAction(), catalogItem); var systemParametersActionWrapper = addSystemParametersToAction(resolvedActionWrapper); var requiredCatalogItemParamsWrapper = addMandatoryCatalogItemParamsIfMissing(systemParametersActionWrapper, catalogItem); - provisionerActionsApiValidator.validate(requiredCatalogItemParamsWrapper.toProvisionAction(), catalogItem); + provisionerActionsApiValidator.validateMandatoryFields(requiredCatalogItemParamsWrapper.toProvisionAction(), catalogItem); var updateProvisionActionWithoutPlaceholdersWrapper = placeholderPostProcessor.process(requiredCatalogItemParamsWrapper); var updatedProvisionActionWithOdsApiParametersWrapper = replaceParametersService.replaceProvisioningParametersFromOdsApi(updateProvisionActionWithoutPlaceholdersWrapper); 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 20b05214..c338a939 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 @@ -9,6 +9,8 @@ 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; @@ -24,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; @@ -61,7 +66,7 @@ void validate_throwsInvalidRestEntityException_whenRequiredParameterMissing(Stri var action = buildActionMissing(missingParam); assertThrows(InvalidRestEntityException.class, - () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); + () -> provisionerActionsApiValidator.validate(action)); } @Test @@ -88,7 +93,7 @@ void validate_throwsProjectComponentAlreadyProvisionedException_whenComponentAlr .thenReturn(List.of(exists)); assertThrows(ProjectComponentAlreadyProvisionedException.class, - () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); + () -> provisionerActionsApiValidator.validate(action)); } @Test @@ -125,7 +130,7 @@ void validate_throwsUserNotAllowedException_whenUserHasNoPermissions() { // Expect exception assertThrows(UserNotAllowedException.class, - () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); + () -> provisionerActionsApiValidator.validate(action)); } @Test @@ -161,7 +166,7 @@ void validate_allowsProvision_whenUserHasPermissions() { .thenReturn(Pair.of(true, "")); // Should NOT throw - provisionerActionsApiValidator.validate(action, new CatalogItem()); + provisionerActionsApiValidator.validate(action); } @Test @@ -174,7 +179,7 @@ void validate_throwsInvalidRestEntityException_whenProjectKeyIsBlank() { )); assertThrows(InvalidRestEntityException.class, - () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); + () -> provisionerActionsApiValidator.validate(action)); } @Test @@ -187,7 +192,7 @@ void validate_throwsInvalidRestEntityException_whenComponentIdIsBlank() { )); assertThrows(InvalidRestEntityException.class, - () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); + () -> provisionerActionsApiValidator.validate(action)); } @Test @@ -200,7 +205,7 @@ void validate_throwsInvalidRestEntityException_whenAccessTokenIsBlank() { )); assertThrows(InvalidRestEntityException.class, - () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); + () -> provisionerActionsApiValidator.validate(action)); } @Test @@ -212,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(), any()); assertThrows(InvalidRestEntityException.class, - () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); + () -> provisionerActionsApiValidator.validateMandatoryFields(action, new CatalogItem())); } @Test @@ -239,7 +238,7 @@ void validate_throwsException_whenComponentCatalogServiceThrowsDuringProvisionCh when(componentCatalogService.getProjectComponents(any(), any())).thenThrow(new RuntimeException("Service error")); assertThrows(RuntimeException.class, - () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); + () -> provisionerActionsApiValidator.validate(action)); } @Test @@ -256,7 +255,7 @@ void validate_throwsException_whenProjectsInfoServiceThrowsDuringPermissionsChec when(projectsInfoService.getProjectGroups(any())).thenThrow(new RuntimeException("Service error")); assertThrows(RuntimeException.class, - () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); + () -> provisionerActionsApiValidator.validate(action)); } @Test @@ -274,7 +273,7 @@ void validate_throwsException_whenGroupsRestrictionsEvaluatorThrowsDuringPermiss when(groupsRestrictionsEvaluator.evaluate(any(), any())).thenThrow(new RuntimeException("Evaluator error")); assertThrows(RuntimeException.class, - () -> provisionerActionsApiValidator.validate(action, new CatalogItem())); + () -> provisionerActionsApiValidator.validate(action)); } private ProvisionAction buildActionMissing(String missingParamName) { @@ -290,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)); + } }