diff options
9 files changed, 264 insertions, 141 deletions
diff --git a/workflow-designer-be/pom.xml b/workflow-designer-be/pom.xml index 58041472..65f239ad 100644 --- a/workflow-designer-be/pom.xml +++ b/workflow-designer-be/pom.xml @@ -17,6 +17,8 @@ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <onap.version>1.3.0-SNAPSHOT</onap.version> + <org.mapstruct.version>1.2.0.Final</org.mapstruct.version> + <org.projectlombok.version>1.18.0</org.projectlombok.version> </properties> <dependencies> @@ -42,15 +44,12 @@ <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> + <version>${org.projectlombok.version}</version> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-actuator</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> @@ -101,6 +100,17 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct-jdk8</artifactId> + <version>${org.mapstruct.version}</version> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <version>RELEASE</version> + <scope>test</scope> + </dependency> </dependencies> @@ -110,8 +120,28 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.5.1</version> + <configuration> + <source>${java.version}</source> + <target>${java.version}</target> + <annotationProcessorPaths> + <path> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct-processor</artifactId> + <version>${org.mapstruct.version}</version> + </path> + <path> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>${lombok.version}</version> + </path> + </annotationProcessorPaths> + </configuration> + </plugin> </plugins> </build> +</project> - -</project>
\ No newline at end of file diff --git a/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/UniqueValueService.java b/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/UniqueValueService.java index e6e88ed9..9a8eee2f 100644 --- a/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/UniqueValueService.java +++ b/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/UniqueValueService.java @@ -1,6 +1,8 @@ package org.onap.sdc.workflow.services; +import com.google.common.annotations.VisibleForTesting; import java.util.Optional; +import org.apache.commons.lang.ArrayUtils; import org.onap.sdc.workflow.persistence.UniqueValueRepository; import org.onap.sdc.workflow.persistence.types.UniqueValueEntity; import org.onap.sdc.workflow.services.errors.UniqueValueViolationException; @@ -26,7 +28,7 @@ public class UniqueValueService { * @param type the type * @param uniqueCombination the unique combination */ - public void createUniqueValue(String type, String... uniqueCombination) { + public void createUniqueValue(String type, String[] uniqueCombination) { formatValue(uniqueCombination).ifPresent(formattedValue -> { validateUniqueValue(type, formattedValue, uniqueCombination); uniqueValueRepository.insert(new UniqueValueEntity(type, formattedValue)); @@ -39,7 +41,7 @@ public class UniqueValueService { * @param type the type * @param uniqueCombination the unique combination */ - public void deleteUniqueValue(String type, String... uniqueCombination) { + public void deleteUniqueValue(String type, String[] uniqueCombination) { formatValue(uniqueCombination) .ifPresent(formattedValue -> uniqueValueRepository.delete(new UniqueValueEntity(type, formattedValue))); @@ -53,7 +55,7 @@ public class UniqueValueService { * @param newValue the new value * @param uniqueContext the unique context */ - public void updateUniqueValue(String type, String oldValue, String newValue, String... uniqueContext) { + public void updateUniqueValue(String type, String oldValue, String newValue, String ... uniqueContext) { if (newValue == null || !newValue.equalsIgnoreCase(oldValue)) { createUniqueValue(type, CommonMethods.concat(uniqueContext, new String[] {newValue})); deleteUniqueValue(type, CommonMethods.concat(uniqueContext, new String[] {oldValue})); @@ -66,7 +68,7 @@ public class UniqueValueService { * @param type the type * @param uniqueCombination the unique combination */ - public void validateUniqueValue(String type, String... uniqueCombination) { + public void validateUniqueValue(String type, String[] uniqueCombination) { formatValue(uniqueCombination) .ifPresent(formattedValue -> validateUniqueValue(type, formattedValue, uniqueCombination)); } @@ -76,12 +78,12 @@ public class UniqueValueService { * * @return true if the unique value is occupied, false otherwise */ - public boolean isUniqueValueOccupied(String type, String... uniqueCombination) { + public boolean isUniqueValueOccupied(String type, String[] uniqueCombination) { return formatValue(uniqueCombination).map(formattedValue -> isUniqueValueOccupied(type, formattedValue)) .orElse(false); } - private void validateUniqueValue(String type, String formattedValue, String... uniqueCombination) { + private void validateUniqueValue(String type, String formattedValue, String[] uniqueCombination) { if (isUniqueValueOccupied(type, formattedValue)) { throw new UniqueValueViolationException(type, getValueWithoutContext(uniqueCombination)); } @@ -91,9 +93,8 @@ public class UniqueValueService { return uniqueValueRepository.findById(new UniqueValueEntity(type, formattedValue)).isPresent(); } - private static Optional<String> formatValue(String[] uniqueCombination) { - if (uniqueCombination == null || uniqueCombination.length == 0 - || getValueWithoutContext(uniqueCombination) == null) { + private Optional<String> formatValue(String[] uniqueCombination) { + if (ArrayUtils.isEmpty(uniqueCombination) || getValueWithoutContext(uniqueCombination) == null) { return Optional.empty(); } @@ -101,7 +102,7 @@ public class UniqueValueService { return Optional.of(CommonMethods.arrayToSeparatedString(uniqueCombination, FORMATTED_UNIQUE_VALUE_SEPARATOR)); } - private static String getValueWithoutContext(String... uniqueCombination) { + private String getValueWithoutContext(String[] uniqueCombination) { return uniqueCombination[uniqueCombination.length - 1]; } } diff --git a/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/WorkflowManagerImpl.java b/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/WorkflowManagerImpl.java index 40750f10..2afaaa0d 100644 --- a/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/WorkflowManagerImpl.java +++ b/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/WorkflowManagerImpl.java @@ -2,12 +2,11 @@ package org.onap.sdc.workflow.services.impl; import java.util.Collection; import java.util.stream.Collectors; +import org.onap.sdc.workflow.services.mappers.WorkflowMapper; import org.onap.sdc.workflow.persistence.types.Workflow; import org.onap.sdc.workflow.services.UniqueValueService; import org.onap.sdc.workflow.services.WorkflowManager; import org.onap.sdc.workflow.services.errors.WorkflowNotFoundException; -import org.onap.sdc.workflow.services.impl.mappers.ItemToWorkflowMapper; -import org.onap.sdc.workflow.services.impl.mappers.WorkflowToItemMapper; import org.openecomp.sdc.versioning.ItemManager; import org.openecomp.sdc.versioning.types.Item; import org.openecomp.sdc.versioning.types.ItemStatus; @@ -22,39 +21,39 @@ public class WorkflowManagerImpl implements WorkflowManager { private static final String WORKFLOW_NAME_UNIQUE_TYPE = "WORKFLOW_NAME"; private final ItemManager itemManager; private final UniqueValueService uniqueValueService; + private WorkflowMapper workflowMapper; @Autowired public WorkflowManagerImpl(ItemManager itemManager, - @Qualifier("uniqueValueService") UniqueValueService uniqueValueService) { + @Qualifier("uniqueValueService") UniqueValueService uniqueValueService, WorkflowMapper workflowMapper) { this.itemManager = itemManager; this.uniqueValueService = uniqueValueService; + this.workflowMapper = workflowMapper; } @Override public Collection<Workflow> list() { - ItemToWorkflowMapper mapper = new ItemToWorkflowMapper(); return itemManager.list(item -> WORKFLOW_TYPE.equals(item.getType())).stream() - .map(item -> mapper.applyMapping(item, Workflow.class)).collect(Collectors.toList()); + .map(item -> workflowMapper.itemToWorkflow(item)).collect(Collectors.toList()); } @Override public Workflow get(Workflow workflow) { - ItemToWorkflowMapper mapper = new ItemToWorkflowMapper(); Item retrievedItem = itemManager.get(workflow.getId()); if (retrievedItem == null) { throw new WorkflowNotFoundException(workflow.getId()); } - return mapper.applyMapping(retrievedItem, Workflow.class); + return this.workflowMapper.itemToWorkflow(retrievedItem); } @Override public void create(Workflow workflow) { - Item item = new WorkflowToItemMapper().applyMapping(workflow, Item.class); + Item item = workflowMapper.workflowToItem(workflow); item.setStatus(ItemStatus.ACTIVE); - uniqueValueService.validateUniqueValue(WORKFLOW_NAME_UNIQUE_TYPE, workflow.getName()); + uniqueValueService.validateUniqueValue(WORKFLOW_NAME_UNIQUE_TYPE, new String[]{workflow.getName()}); workflow.setId(itemManager.create(item).getId()); - uniqueValueService.createUniqueValue(WORKFLOW_NAME_UNIQUE_TYPE, workflow.getName()); + uniqueValueService.createUniqueValue(WORKFLOW_NAME_UNIQUE_TYPE, new String[]{workflow.getName()}); } @Override @@ -66,7 +65,7 @@ public class WorkflowManagerImpl implements WorkflowManager { uniqueValueService.updateUniqueValue(WORKFLOW_NAME_UNIQUE_TYPE, retrievedItem.getName(), workflow.getName()); - Item item = new WorkflowToItemMapper().applyMapping(workflow, Item.class); + Item item = workflowMapper.workflowToItem(workflow); item.setId(workflow.getId()); item.setStatus(retrievedItem.getStatus()); item.setVersionStatusCounters(retrievedItem.getVersionStatusCounters()); diff --git a/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/mappers/ItemToWorkflowMapper.java b/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/mappers/ItemToWorkflowMapper.java deleted file mode 100644 index 09e3cbd6..00000000 --- a/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/mappers/ItemToWorkflowMapper.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.onap.sdc.workflow.services.impl.mappers; - -import org.onap.sdc.workflow.persistence.types.Workflow; -import org.onap.sdc.workflow.persistence.types.WorkflowProperty; -import org.openecomp.sdc.versioning.types.Item; - -public class ItemToWorkflowMapper extends Mapper<Item, Workflow> { - - @Override - public void map(Item source, Workflow target) { - target.setId(source.getId()); - target.setName(source.getName()); - target.setDescription(source.getDescription()); - target.setCategory((String) source.getProperties().get(WorkflowProperty.CATEGORY)); - } -} diff --git a/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/mappers/Mapper.java b/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/mappers/Mapper.java deleted file mode 100644 index a66f8608..00000000 --- a/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/mappers/Mapper.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.onap.sdc.workflow.services.impl.mappers; - -import org.openecomp.sdc.common.errors.CoreException; -import org.openecomp.sdc.common.errors.ErrorCode; - -/** - * Base class for all mapping classes. Mapping classes will perform data mapping from source object - * to target object Base class provides following<br> <ol> <li>provides life cycle of - * mapping class , first mapSimpleProperties is called and then mapComplexProperties is - * called.</li> <li>methods mapSimpleProperties and mapComplexProperties with default - * implementation, these should be overridden by concrete mapping classes for writing mapping - * logic.</li> </ol> - */ - -public abstract class Mapper<S, T> { - - /** - * Method is called for starting mapping from source object to target object method sets context - * in the thread locale and than calls mapSimpleProperties and mapComplexProperties - * respectively. - * - * @param source : source object for mapping - * @param clazz : target <code>Class</code> for mapping - * @return <code>T</code> - instance of type <code>T</code> - */ - - public final T applyMapping(final S source, Class<T> clazz) { - T target = (T) instantiateTarget(clazz); - if (source != null && target != null) { - preMapping(source, target); - map(source, target); - postMapping(source, target); - - } - return target; - - } - - /** - * This method is called before the <code>map</code> method. - */ - protected void preMapping(final S source, T target) { - // extension point - } - - /** - * The actual method that does the mapping between the <code>source</code> to <code>target</code> - * objects. This method is being called automatically as part of the mapper class. This - * method must be override (it is abstract) by the mapper class. - * - * @param source - the source object. - * @param target - the target object. - */ - - public abstract void map(final S source, T target); - - /** - * This method is called after the <code>map</code> method. - */ - protected void postMapping(final S source, T target) { - // extension point - } - - /** - * Creates the instance of the input class. - * - * @return <code>Object</code> - */ - - private Object instantiateTarget(final Class<?> clazz) { - - try { - return clazz.newInstance(); - } catch (InstantiationException | IllegalAccessException exception) { - throw new CoreException((new ErrorCode.ErrorCodeBuilder()).withMessage(exception.getMessage()).build(), - exception); - } - } -} - diff --git a/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/mappers/WorkflowToItemMapper.java b/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/mappers/WorkflowToItemMapper.java deleted file mode 100644 index 0b569f09..00000000 --- a/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/impl/mappers/WorkflowToItemMapper.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.onap.sdc.workflow.services.impl.mappers; - -import static org.onap.sdc.workflow.services.impl.WorkflowManagerImpl.WORKFLOW_TYPE; - -import org.onap.sdc.workflow.persistence.types.Workflow; -import org.onap.sdc.workflow.persistence.types.WorkflowProperty; -import org.openecomp.sdc.versioning.types.Item; - -public class WorkflowToItemMapper extends Mapper<Workflow, Item> { - - @Override - public void map(Workflow source, Item target) { - target.setType(WORKFLOW_TYPE); - target.setName(source.getName()); - target.setDescription(source.getDescription()); - target.addProperty(WorkflowProperty.CATEGORY, source.getCategory()); - } -} diff --git a/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/mappers/WorkflowMapper.java b/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/mappers/WorkflowMapper.java new file mode 100644 index 00000000..c603908b --- /dev/null +++ b/workflow-designer-be/src/main/java/org/onap/sdc/workflow/services/mappers/WorkflowMapper.java @@ -0,0 +1,32 @@ +package org.onap.sdc.workflow.services.mappers; + +import java.util.Collections; +import java.util.Map; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.onap.sdc.workflow.persistence.types.Workflow; +import org.onap.sdc.workflow.persistence.types.WorkflowProperty; +import org.openecomp.sdc.versioning.types.Item; + +@Mapper(componentModel = "spring") +public interface WorkflowMapper { + + @Mapping(source = "properties", target = "category", qualifiedByName = "propertiesToCategoryMapper") + Workflow itemToWorkflow(Item item); + + @Mapping(source = "category", target = "properties", qualifiedByName = "categoryToPropertiesMapper") + @InheritInverseConfiguration + Item workflowToItem(Workflow workflow); + + @Named("propertiesToCategoryMapper") + default String customPropertiesToCategoryMapper(Map<String, Object> properties) { + return String.class.cast(properties.get(WorkflowProperty.CATEGORY)); + } + + @Named("categoryToPropertiesMapper") + default Map<String, Object> customCategoryToPropertiesMapper(String category) { + return Collections.singletonMap(WorkflowProperty.CATEGORY, category); + } +} diff --git a/workflow-designer-be/src/test/org/onap/sdc/workflow/api/mapping/WorkflowMapperTest.java b/workflow-designer-be/src/test/org/onap/sdc/workflow/api/mapping/WorkflowMapperTest.java new file mode 100644 index 00000000..3d5784bc --- /dev/null +++ b/workflow-designer-be/src/test/org/onap/sdc/workflow/api/mapping/WorkflowMapperTest.java @@ -0,0 +1,64 @@ +package org.onap.sdc.workflow.api.mapping; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.HashMap; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.onap.sdc.workflow.persistence.types.Workflow; +import org.onap.sdc.workflow.persistence.types.WorkflowProperty; +import org.onap.sdc.workflow.services.mappers.WorkflowMapper; +import org.openecomp.sdc.versioning.types.Item; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@ContextConfiguration(classes = WorkflowMapperTest.WorkflowMapperSpringTestConfig.class) +@RunWith(SpringJUnit4ClassRunner.class) +public class WorkflowMapperTest { + + @Configuration + @ComponentScan(basePackageClasses = WorkflowMapperTest.class) + public static class WorkflowMapperSpringTestConfig { } + + @Autowired + WorkflowMapper workflowMapper; + + @Test + public void shouldMapItemPropertyToWorkflowCategory() { + + Item item = createMockItem(); + HashMap<String, Object> properties = new HashMap<>(); + properties.put(WorkflowProperty.CATEGORY, "category"); + item.setProperties(properties); + + Workflow mappedWorkflow = workflowMapper.itemToWorkflow(item); + assertEquals(mappedWorkflow.getId(), item.getId()); + assertEquals(mappedWorkflow.getDescription(), item.getDescription()); + assertEquals(mappedWorkflow.getName(), item.getName()); + assertEquals(mappedWorkflow.getCategory(), properties.get(WorkflowProperty.CATEGORY)); + } + + @Test + public void shouldAddWorkflowCategoryToItemProperties(){ + Workflow workflow = new Workflow(); + workflow.setId("id"); + workflow.setCategory("cat"); + + Item item = workflowMapper.workflowToItem(workflow); + assertNotNull(item.getProperties().get(WorkflowProperty.CATEGORY)); + } + + private Item createMockItem() { + Item item = new Item(); + item.setId("id"); + item.setDescription("item description"); + item.setName("item name"); + return item; + } + +}
\ No newline at end of file diff --git a/workflow-designer-be/src/test/org/onap/sdc/workflow/services/UniqueValueServiceTest.java b/workflow-designer-be/src/test/org/onap/sdc/workflow/services/UniqueValueServiceTest.java new file mode 100644 index 00000000..57d06fcb --- /dev/null +++ b/workflow-designer-be/src/test/org/onap/sdc/workflow/services/UniqueValueServiceTest.java @@ -0,0 +1,111 @@ +package org.onap.sdc.workflow.services; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Optional; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; +import org.onap.sdc.workflow.persistence.UniqueValueRepository; +import org.onap.sdc.workflow.persistence.types.UniqueValueEntity; +import org.onap.sdc.workflow.services.errors.UniqueValueViolationException; + +public class UniqueValueServiceTest { + + public static final String TYPE = "ss"; + public static final String DUMMY_COMBINATION = "dummy"; + + @Mock + private UniqueValueRepository uniqueValueRepositoryMock; + + @Spy + @InjectMocks + private UniqueValueService uniqueValueService; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + // testing create unique value- START + @Test + public void shouldCallRepositoryInsertIfValueUnique(){ + doReturn(Optional.empty()).when(uniqueValueRepositoryMock).findById(any()); + uniqueValueService.createUniqueValue(TYPE, new String[]{DUMMY_COMBINATION}); + verify(uniqueValueRepositoryMock, times(1)).insert(any(UniqueValueEntity.class)); + } + + @Test + public void shouldNotCheckValueIfNoUniqueCombination(){ + uniqueValueService.createUniqueValue(TYPE, null); + verify(uniqueValueRepositoryMock, never()).findById(any(UniqueValueEntity.class)); + } + + @Test(expected = UniqueValueViolationException.class) + public void shouldThrowExceptionIfValueIsNotUnique(){ + doReturn(Optional.of("xxx")).when(uniqueValueRepositoryMock).findById(any()); + uniqueValueService.createUniqueValue(TYPE, new String[]{DUMMY_COMBINATION}); + } + // testing create unique value- END + + // testing delete unique value- START + @Test + public void shouldCallRepositoryDeleteIfValueValid(){ + uniqueValueService.deleteUniqueValue(TYPE, new String[]{DUMMY_COMBINATION}); + verify(uniqueValueRepositoryMock, times(1)).delete(any(UniqueValueEntity.class)); + } + + @Test + public void shouldNotCallRepositoryDeleteIfValueNouniqueCombination(){ + uniqueValueService.deleteUniqueValue(TYPE, new String[]{}); + verify(uniqueValueRepositoryMock, never()).delete(any(UniqueValueEntity.class)); + } + + // testing delete unique value- END + + // testing update unique value- START + @Test + public void shouldNotUpdateIfNewAndOldValueAreEqualsCaseIgnore(){ + String value = "value"; + uniqueValueService.updateUniqueValue(TYPE, value, value.toUpperCase()); + verify(uniqueValueService, never()).createUniqueValue(anyString(), any()); + } + + @Test + public void shouldUpdateIfNewAndOldValueAreNotEqualsCaseIgnore(){ + String oldValue = "oldValue"; + String newValue = "newValue"; + uniqueValueService.updateUniqueValue(TYPE, oldValue, newValue); + verify(uniqueValueService, times(1)).createUniqueValue(anyString(), any()); + verify(uniqueValueService, times(1)).deleteUniqueValue(anyString(), any()); + } + // testing update unique value- END + + // testing validateUniqueValue- START + @Test + public void shouldReturnTrueIfValueExist() { + doReturn(Optional.of("xxx")).when(uniqueValueRepositoryMock).findById(any()); + assertTrue(uniqueValueService.isUniqueValueOccupied(TYPE, new String[]{DUMMY_COMBINATION})); + } + + @Test + public void shouldReturnFalseIfValueNotExist() { + doReturn(Optional.empty()).when(uniqueValueRepositoryMock).findById(any()); + assertFalse(uniqueValueService.isUniqueValueOccupied(TYPE, new String[]{DUMMY_COMBINATION})); + } + + // testing validate unique value- END + + +} |