diff --git a/.gitignore b/.gitignore index 8b6c1c386122d1f8e835ffe797eab68b340c88ae..b5e3b3eb7880f6430da0193ef5916d86e8e99808 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ target/ .settings/ .project .classpath -bin/ \ No newline at end of file +bin/ +.vscode/ \ No newline at end of file diff --git a/persistence/runtime/pom.xml b/persistence/runtime/pom.xml index 85d8fd5e3da5b46748fc35def7afa0bf7999a49e..636f33decc6dece653c14bba13e9c643faa2500c 100644 --- a/persistence/runtime/pom.xml +++ b/persistence/runtime/pom.xml @@ -8,11 +8,15 @@ quarkus-persistence Persistence - Runtime + + 1.5.2.Final + 1.8.2 + org.eclipsefoundation quarkus-core - ${project.version} + ${project.version} io.quarkus @@ -30,6 +34,11 @@ org.apache.commons commons-lang3 + + org.mapstruct + mapstruct + ${org.mapstruct.version} + @@ -57,6 +66,24 @@ h2 test + + + + com.google.auto.value + auto-value + ${auto-value.version} + provided + + + com.google.auto.value + auto-value-annotations + ${auto-value.version} + + + com.google.code.findbugs + jsr305 + + @@ -75,7 +102,7 @@ - + maven-compiler-plugin @@ -85,6 +112,16 @@ quarkus-extension-processor ${quarkus.version} + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + com.google.auto.value + auto-value + ${auto-value.version} + diff --git a/persistence/runtime/src/main/java/org/eclipsefoundation/persistence/config/QuarkusMappingConfig.java b/persistence/runtime/src/main/java/org/eclipsefoundation/persistence/config/QuarkusMappingConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..37b0c4fa67d6c12dc520d48509efabe2d44d5a4e --- /dev/null +++ b/persistence/runtime/src/main/java/org/eclipsefoundation/persistence/config/QuarkusMappingConfig.java @@ -0,0 +1,17 @@ +/********************************************************************* +* Copyright (c) 2022 Eclipse Foundation +* +* This program and the accompanying materials are made +* available under the terms of the Eclipse Public License 2.0 +* which is available at https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +**********************************************************************/ +package org.eclipsefoundation.persistence.config; + +import org.mapstruct.MapperConfig; + +@MapperConfig(componentModel = "cdi") +public interface QuarkusMappingConfig { + +} diff --git a/persistence/runtime/src/main/java/org/eclipsefoundation/persistence/dto/mapper/EntityMapper.java b/persistence/runtime/src/main/java/org/eclipsefoundation/persistence/dto/mapper/EntityMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..9b6b13d2eff626ee4df6fe601b0241218b7e2e7a --- /dev/null +++ b/persistence/runtime/src/main/java/org/eclipsefoundation/persistence/dto/mapper/EntityMapper.java @@ -0,0 +1,40 @@ +/********************************************************************* +* Copyright (c) 2022 Eclipse Foundation +* +* This program and the accompanying materials are made +* available under the terms of the Eclipse Public License 2.0 +* which is available at https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +**********************************************************************/ +package org.eclipsefoundation.persistence.dto.mapper; + +import org.eclipsefoundation.persistence.dto.BareNode; +import org.mapstruct.InheritInverseConfiguration; + +/** + * Mapper interface to allow mapping of DTOs and Models + * + * @author Zachary Sabourin + */ +public interface EntityMapper { + + S toModel(T dtoEntity); + + @InheritInverseConfiguration + T toDTO(S model); + + /** + * Returns the mapped DTO type. + * + * @return the class of mapped DTO + */ + Class getDTOType(); + + /** + * Returns the mapped model type. + * + * @return the class of mapped model + */ + Class getModelType(); +} diff --git a/persistence/runtime/src/main/java/org/eclipsefoundation/persistence/service/MapperService.java b/persistence/runtime/src/main/java/org/eclipsefoundation/persistence/service/MapperService.java new file mode 100644 index 0000000000000000000000000000000000000000..5eb74dbd4d371a4943500f1f67d143206b0a93f9 --- /dev/null +++ b/persistence/runtime/src/main/java/org/eclipsefoundation/persistence/service/MapperService.java @@ -0,0 +1,40 @@ +/********************************************************************* +* Copyright (c) 2022 Eclipse Foundation +* +* This program and the accompanying materials are made +* available under the terms of the Eclipse Public License 2.0 +* which is available at https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +**********************************************************************/ +package org.eclipsefoundation.persistence.service; + +import org.eclipsefoundation.persistence.dto.BareNode; +import org.eclipsefoundation.persistence.dto.mapper.EntityMapper; + +/** + * MapperService interface to allow retrieval of a corresponding EntityMapper. + * Can retrieve an EntityMapper based on target DTO or model class. + * + * @author Zachary Sabourin + */ +public interface MapperService { + + /** + * Method to allow retrieval of EntityMapper that corresponds to the target + * model class. + * + * @param target The target class used for EntityMapper retrieval + * @return A reference to the target EntityMapper + */ + EntityMapper getByModel(Class target); + + /** + * Method to allow retrieval of EntityMapper that corresponds to the target + * DTO class. + * + * @param target The target class used for EntityMapper retrieval + * @return A reference to the target EntityMapper + */ + EntityMapper getByDTO(Class target); +} diff --git a/persistence/runtime/src/main/java/org/eclipsefoundation/persistence/service/impl/DefaultMapperService.java b/persistence/runtime/src/main/java/org/eclipsefoundation/persistence/service/impl/DefaultMapperService.java new file mode 100644 index 0000000000000000000000000000000000000000..19666875d95f88c174ecfd4b9bdd870c54d262cc --- /dev/null +++ b/persistence/runtime/src/main/java/org/eclipsefoundation/persistence/service/impl/DefaultMapperService.java @@ -0,0 +1,61 @@ +/********************************************************************* +* Copyright (c) 2022 Eclipse Foundation +* +* This program and the accompanying materials are made +* available under the terms of the Eclipse Public License 2.0 +* which is available at https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +**********************************************************************/ +package org.eclipsefoundation.persistence.service.impl; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Instance; +import javax.inject.Inject; + +import org.eclipsefoundation.persistence.dto.BareNode; +import org.eclipsefoundation.persistence.dto.mapper.EntityMapper; +import org.eclipsefoundation.persistence.service.MapperService; + +/** + * Indicates an entity mapping between DTOs and models + * + * @author Zachary Sabourin + * + * @param the DTO object in the mapping pair + * @param the model object in the mapping pair + */ +@ApplicationScoped +public class DefaultMapperService implements MapperService { + + @Inject + Instance> mappers; + + /** + * Retrieves an instance of EntityMapper that corresponds to the target model + * class. + * + * @param target The target class used for EntityMapper retrieval + * @return A reference to the target EntityMapper if it exists + */ + @Override + @SuppressWarnings("unchecked") + public EntityMapper getByModel(Class target) { + return (EntityMapper) mappers.stream().filter(map -> map.getModelType().equals(target)) + .findFirst().orElse(null); + } + + /** + * Retrieves an instance of EntityMapper that corresponds to the target DTO + * class. + * + * @param target The target class used for EntityMapper retrieval + * @return A reference to the target EntityMapper if it exists + */ + @Override + @SuppressWarnings("unchecked") + public EntityMapper getByDTO(Class target) { + return (EntityMapper) mappers.stream().filter(map -> map.getDTOType().equals(target)) + .findFirst().orElse(null); + } +} diff --git a/persistence/runtime/src/test/java/org/eclipsefoundation/persistence/service/MapperServiceTest.java b/persistence/runtime/src/test/java/org/eclipsefoundation/persistence/service/MapperServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..598dd8753b1807b88e8461d9342f00a5f7ce822c --- /dev/null +++ b/persistence/runtime/src/test/java/org/eclipsefoundation/persistence/service/MapperServiceTest.java @@ -0,0 +1,97 @@ +/********************************************************************* +* Copyright (c) 2022 Eclipse Foundation +* +* This program and the accompanying materials are made +* available under the terms of the Eclipse Public License 2.0 +* which is available at https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +**********************************************************************/ +package org.eclipsefoundation.persistence.service; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import javax.inject.Inject; + +import org.eclipsefoundation.persistence.test.dto.PersonDTO; +import org.eclipsefoundation.persistence.test.model.PersonModel; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +/** + * Tests the MapperService by attempting to retrieve a Mapper by model and DTO. + * Tests that the retrieved Mapper converts properly in both directions. + * + * @author Zachary Sabourin + */ +@QuarkusTest +public class MapperServiceTest { + + @Inject + MapperService mapperService; + + @Test + public void getMapperByModel() { + assertTrue(mapperService.getByModel(PersonModel.class) != null); + assertTrue(mapperService.getByModel(PersonModel.class).getDTOType() == PersonDTO.class); + } + + @Test + public void getMapperByDTO() { + assertTrue(mapperService.getByDTO(PersonDTO.class) != null); + assertTrue(mapperService.getByDTO(PersonDTO.class).getModelType() == PersonModel.class); + } + + @Test + public void convertToDTO_mapperByModel() { + PersonModel personModel = PersonModel.builder().setAge(25).setName("JimBob").setPersonID("TM-50").build(); + + PersonDTO personDTO = mapperService.getByModel(PersonModel.class).toDTO(personModel); + + assertTrue(personModel.getPersonID().equals(personDTO.getId())); + assertTrue(personModel.getName().equals(personDTO.getName())); + assertTrue(personModel.getAge() == personDTO.getAge()); + } + + @Test + public void convertToDTO_mapperByDTO() { + PersonModel personModel = PersonModel.builder().setAge(25).setName("JimBob").setPersonID("TM-50").build(); + + PersonDTO personDTO = mapperService.getByDTO(PersonDTO.class).toDTO(personModel); + + assertTrue(personModel.getPersonID().equals(personDTO.getId())); + assertTrue(personModel.getName().equals(personDTO.getName())); + assertTrue(personModel.getAge() == personDTO.getAge()); + } + + @Test + public void convertToModel_mapperbyModel() { + + PersonDTO personDTO = new PersonDTO(); + personDTO.setPersonID("TM-50"); + personDTO.setName("JimBob"); + personDTO.setAge(25); + + PersonModel personModel = mapperService.getByModel(PersonModel.class).toModel(personDTO); + + assertTrue(personModel.getPersonID().equals(personDTO.getId())); + assertTrue(personModel.getName().equals(personDTO.getName())); + assertTrue(personModel.getAge() == personDTO.getAge()); + } + + @Test + public void convertToModel_mapperbyDTO() { + + PersonDTO personDTO = new PersonDTO(); + personDTO.setPersonID("TM-50"); + personDTO.setName("JimBob"); + personDTO.setAge(25); + + PersonModel personModel = mapperService.getByDTO(PersonDTO.class).toModel(personDTO); + + assertTrue(personModel.getPersonID().equals(personDTO.getId())); + assertTrue(personModel.getName().equals(personDTO.getName())); + assertTrue(personModel.getAge() == personDTO.getAge()); + } +} diff --git a/persistence/runtime/src/test/java/org/eclipsefoundation/persistence/service/dto/mapper/PersonMapper.java b/persistence/runtime/src/test/java/org/eclipsefoundation/persistence/service/dto/mapper/PersonMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..af695a719e9cca7ac8dac8bd737f3f2493d4a5f9 --- /dev/null +++ b/persistence/runtime/src/test/java/org/eclipsefoundation/persistence/service/dto/mapper/PersonMapper.java @@ -0,0 +1,40 @@ +/********************************************************************* +* Copyright (c) 2022 Eclipse Foundation +* +* This program and the accompanying materials are made +* available under the terms of the Eclipse Public License 2.0 +* which is available at https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +**********************************************************************/ +package org.eclipsefoundation.persistence.service.dto.mapper; + +import org.eclipsefoundation.persistence.config.QuarkusMappingConfig; +import org.eclipsefoundation.persistence.dto.mapper.EntityMapper; +import org.eclipsefoundation.persistence.test.dto.PersonDTO; +import org.eclipsefoundation.persistence.test.model.PersonModel; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; + +/** + * A Mapper used for testing. It maps between 2 test entity classes, + * PersonDTO and PersonModel. + */ +@Mapper(config = QuarkusMappingConfig.class) +public interface PersonMapper extends EntityMapper { + + PersonModel toModel(PersonDTO personDTO); + + @InheritInverseConfiguration + PersonDTO toDTO(PersonModel personModel); + + @Override + default Class getDTOType() { + return PersonDTO.class; + } + + @Override + default Class getModelType() { + return PersonModel.class; + } +} \ No newline at end of file diff --git a/persistence/runtime/src/test/java/org/eclipsefoundation/persistence/test/dto/PersonDTO.java b/persistence/runtime/src/test/java/org/eclipsefoundation/persistence/test/dto/PersonDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..b6204093dac233c166e579d45ced355a9a35bc59 --- /dev/null +++ b/persistence/runtime/src/test/java/org/eclipsefoundation/persistence/test/dto/PersonDTO.java @@ -0,0 +1,58 @@ +/********************************************************************* +* Copyright (c) 2022 Eclipse Foundation +* +* This program and the accompanying materials are made +* available under the terms of the Eclipse Public License 2.0 +* which is available at https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +**********************************************************************/ +package org.eclipsefoundation.persistence.test.dto; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Id; + +import org.eclipsefoundation.persistence.dto.BareNode; + +/** + * A basic DTO class with a few simple fields. Used for testing. + */ +public class PersonDTO extends BareNode implements Serializable { + + @Id + @Column(unique = true, nullable = false) + private String personID; + private String name; + private int age; + + @Override + public Object getId() { + return getPersonID(); + } + + public String getPersonID() { + return this.personID; + } + + public void setPersonID(String id) { + this.personID = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return this.age; + } + + public void setAge(int age) { + this.age = age; + } +} \ No newline at end of file diff --git a/persistence/runtime/src/test/java/org/eclipsefoundation/persistence/test/model/PersonModel.java b/persistence/runtime/src/test/java/org/eclipsefoundation/persistence/test/model/PersonModel.java new file mode 100644 index 0000000000000000000000000000000000000000..7023d37e0546e0d368db810a9d3b8075d545bd99 --- /dev/null +++ b/persistence/runtime/src/test/java/org/eclipsefoundation/persistence/test/model/PersonModel.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (C) 2022 Eclipse Foundation + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ******************************************************************************/ +package org.eclipsefoundation.persistence.test.model; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.google.auto.value.AutoValue; + +/** + * A basic Model class with a few simple fields. Used for testing. + */ +@AutoValue +@JsonDeserialize(builder = AutoValue_PersonModel.Builder.class) +public abstract class PersonModel { + public abstract String getPersonID(); + + public abstract String getName(); + + public abstract int getAge(); + + public static Builder builder() { + return new AutoValue_PersonModel.Builder(); + } + + @AutoValue.Builder + @JsonPOJOBuilder(withPrefix = "set") + public abstract static class Builder { + public abstract Builder setPersonID(String personID); + + public abstract Builder setName(String name); + + public abstract Builder setAge(int age); + + public abstract PersonModel build(); + } +} \ No newline at end of file