diff --git a/src/main/java/com/devonfw/demoquarkus/domain/model/AnimalEntity.java b/src/main/java/com/devonfw/demoquarkus/domain/model/AnimalEntity.java deleted file mode 100644 index 967f347f..00000000 --- a/src/main/java/com/devonfw/demoquarkus/domain/model/AnimalEntity.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.devonfw.demoquarkus.domain.model; - -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.Table; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@Entity -@Table(name="Animal") -// A JPA entity requires at least 2 things @Entity annotation and an ID -// by default, the DB table will have the same name as our class -public class AnimalEntity { - - @Id - @GeneratedValue - private Long id; - - private String name; - - // every primitive attribute on this class will be represented as column in animal table - private String basicInfo; - - private int numberOfLegs; - -} diff --git a/src/main/java/com/devonfw/demoquarkus/domain/model/AnimalSearchCriteria.java b/src/main/java/com/devonfw/demoquarkus/domain/model/AnimalSearchCriteria.java deleted file mode 100644 index 5e52f9fc..00000000 --- a/src/main/java/com/devonfw/demoquarkus/domain/model/AnimalSearchCriteria.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.devonfw.demoquarkus.domain.model; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AnimalSearchCriteria { - - private String name; - - private Integer numberOfLegs; - - private Integer pageNumber; - - private Integer pageSize; -} diff --git a/src/main/java/com/devonfw/demoquarkus/domain/repo/AnimalFragment.java b/src/main/java/com/devonfw/demoquarkus/domain/repo/AnimalFragment.java deleted file mode 100644 index 4bdc967b..00000000 --- a/src/main/java/com/devonfw/demoquarkus/domain/repo/AnimalFragment.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.devonfw.demoquarkus.domain.repo; - -import org.springframework.data.domain.Page; - -import com.devonfw.demoquarkus.domain.model.AnimalEntity; -import com.devonfw.demoquarkus.service.v1.model.AnimalSearchCriteriaDto; - -public interface AnimalFragment { - public Page findAllCriteriaApi(AnimalSearchCriteriaDto dto); - - public Page findAllQueryDsl(AnimalSearchCriteriaDto dto); - - public Page findByNameNativeQuery(AnimalSearchCriteriaDto dto); - - public Page findByNameQuery(AnimalSearchCriteriaDto dto); -} diff --git a/src/main/java/com/devonfw/demoquarkus/domain/repo/AnimalFragmentImpl.java b/src/main/java/com/devonfw/demoquarkus/domain/repo/AnimalFragmentImpl.java deleted file mode 100644 index e96b4a55..00000000 --- a/src/main/java/com/devonfw/demoquarkus/domain/repo/AnimalFragmentImpl.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.devonfw.demoquarkus.domain.repo; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; -import javax.persistence.EntityManager; -import javax.persistence.Query; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; - -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; - -import com.devonfw.demoquarkus.domain.model.AnimalEntity; -import com.devonfw.demoquarkus.domain.model.AnimalEntity_; -import com.devonfw.demoquarkus.domain.model.QAnimalEntity; -import com.devonfw.demoquarkus.service.v1.model.AnimalSearchCriteriaDto; -import com.querydsl.jpa.impl.JPAQuery; - -public class AnimalFragmentImpl implements AnimalFragment { - - @Inject - EntityManager em; - - @Override - public Page findAllCriteriaApi(AnimalSearchCriteriaDto dto) { - - CriteriaQuery cq = this.em.getCriteriaBuilder().createQuery(AnimalEntity.class); - Root root = cq.from(AnimalEntity.class); - List predicates = new ArrayList<>(); - CriteriaBuilder cb = this.em.getCriteriaBuilder(); - if (dto.getName() != null && !dto.getName().isEmpty()) { - predicates.add(cb.like(root.get(AnimalEntity_.NAME), dto.getName())); - } - if (dto.getNumberOfLegs() != null) { - predicates.add(cb.equal(root.get(AnimalEntity_.NUMBER_OF_LEGS), dto.getNumberOfLegs())); - } - if (!predicates.isEmpty()) { - cq.where(predicates.toArray(new Predicate[0])); - } - - // Order by name - cq.orderBy(cb.desc(root.get(AnimalEntity_.NAME))); - - TypedQuery animals = this.em.createQuery(cq).setFirstResult(dto.getPageNumber() * dto.getPageSize()) - .setMaxResults(dto.getPageSize()); - return new PageImpl(animals.getResultList(), PageRequest.of(dto.getPageNumber(), dto.getPageSize()), - animals.getResultList().size()); - } - - @Override - public Page findAllQueryDsl(AnimalSearchCriteriaDto dto) { - - QAnimalEntity animal = QAnimalEntity.animalEntity; - JPAQuery query = new JPAQuery(this.em); - query.from(animal); - if (dto.getName() != null && !dto.getName().isEmpty()) { - query.where(animal.name.eq(dto.getName())); - } - if (dto.getNumberOfLegs() != null) { - query.where(animal.numberOfLegs.eq(dto.getNumberOfLegs())); - } - - // Order by name - query.orderBy(animal.name.desc()); - - List animals = query.limit(dto.getPageSize()).offset(dto.getPageNumber() * dto.getPageSize()).fetch(); - return new PageImpl<>(animals, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), animals.size()); - } - - @Override - public Page findByNameQuery(AnimalSearchCriteriaDto dto) { - - Query query = this.em.createQuery("select a from AnimalEntity a where a.name = :name"); - query.setParameter("name", dto.getName()); - List animals = query.getResultList(); - return new PageImpl<>(animals, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), animals.size()); - } - - @Override - public Page findByNameNativeQuery(AnimalSearchCriteriaDto dto) { - - Query query = this.em.createNativeQuery("select * from AnimalEntity where name = :name", AnimalEntity.class); - query.setParameter("name", dto.getName()); - List animals = query.getResultList(); - return new PageImpl<>(animals, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), animals.size()); - } -} diff --git a/src/main/java/com/devonfw/demoquarkus/domain/repo/AnimalRepository.java b/src/main/java/com/devonfw/demoquarkus/domain/repo/AnimalRepository.java deleted file mode 100644 index 11151c8a..00000000 --- a/src/main/java/com/devonfw/demoquarkus/domain/repo/AnimalRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.devonfw.demoquarkus.domain.repo; - -import org.springframework.data.domain.Page; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.query.Param; - -import com.devonfw.demoquarkus.domain.model.AnimalEntity; - -public interface AnimalRepository extends CrudRepository, AnimalFragment { - - @Query("select a from AnimalEntity a where name = :name") - AnimalEntity findByName(@Param("name") String name); - - Page findAllByOrderByName(); -} diff --git a/src/main/java/com/devonfw/demoquarkus/logic/UcFindAnimal.java b/src/main/java/com/devonfw/demoquarkus/logic/UcFindAnimal.java deleted file mode 100644 index f45c0954..00000000 --- a/src/main/java/com/devonfw/demoquarkus/logic/UcFindAnimal.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.devonfw.demoquarkus.logic; - -import com.devonfw.demoquarkus.service.v1.model.AnimalDto; -import com.devonfw.demoquarkus.service.v1.model.AnimalSearchCriteriaDto; -import org.springframework.data.domain.Page; - -public interface UcFindAnimal { - Page findAnimals(AnimalSearchCriteriaDto dto); - - Page findAnimalsByCriteriaApi(AnimalSearchCriteriaDto dto); - - Page findAnimalsByQueryDsl(AnimalSearchCriteriaDto dto); - - Page findAnimalsByNameQuery(AnimalSearchCriteriaDto dto); - - Page findAnimalsByNameNativeQuery(AnimalSearchCriteriaDto dto); - - Page findAnimalsOrderedByName(); - - AnimalDto findAnimal(String id); - - AnimalDto findAnimalByName(String name); -} diff --git a/src/main/java/com/devonfw/demoquarkus/logic/UcFindAnimalImpl.java b/src/main/java/com/devonfw/demoquarkus/logic/UcFindAnimalImpl.java deleted file mode 100644 index 086f854f..00000000 --- a/src/main/java/com/devonfw/demoquarkus/logic/UcFindAnimalImpl.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.devonfw.demoquarkus.logic; - -import com.devonfw.demoquarkus.domain.model.AnimalEntity; -import com.devonfw.demoquarkus.domain.repo.AnimalRepository; -import com.devonfw.demoquarkus.service.v1.mapper.AnimalMapper; -import com.devonfw.demoquarkus.service.v1.model.AnimalDto; -import com.devonfw.demoquarkus.service.v1.model.AnimalSearchCriteriaDto; -import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.transaction.Transactional; -import java.util.ArrayList; -import java.util.List; - -@Named -@Transactional -@Slf4j -public class UcFindAnimalImpl implements UcFindAnimal { - @Inject - AnimalRepository animalRepository; - - @Inject - AnimalMapper mapper; - - @Override - public Page findAnimals(AnimalSearchCriteriaDto dto) { - Iterable animalsIterator = this.animalRepository.findAll(); - List animals = new ArrayList(); - animalsIterator.forEach(animals::add); - List animalsDto = this.mapper.map(animals); - return new PageImpl<>(animalsDto, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), animalsDto.size()); - } - - @Override - public Page findAnimalsByCriteriaApi(AnimalSearchCriteriaDto dto) { - List animals = this.animalRepository.findAllCriteriaApi(dto).getContent(); - List animalsDto = this.mapper.map(animals); - return new PageImpl<>(animalsDto, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), animalsDto.size()); - } - - @Override - public Page findAnimalsByQueryDsl(AnimalSearchCriteriaDto dto) { - List animals = this.animalRepository.findAllQueryDsl(dto).getContent(); - List animalsDto = this.mapper.map(animals); - return new PageImpl<>(animalsDto, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), animalsDto.size()); - } - - @Override - public Page findAnimalsByNameQuery(AnimalSearchCriteriaDto dto) { - List animals = this.animalRepository.findByNameQuery(dto).getContent(); - List animalsDto = this.mapper.map(animals); - return new PageImpl<>(animalsDto, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), animalsDto.size()); - } - - @Override - public Page findAnimalsByNameNativeQuery(AnimalSearchCriteriaDto dto) { - List animals = this.animalRepository.findByNameNativeQuery(dto).getContent(); - List animalsDto = this.mapper.map(animals); - return new PageImpl<>(animalsDto, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), animalsDto.size()); - } - - @Override - public Page findAnimalsOrderedByName() { - List animals = this.animalRepository.findAllByOrderByName().getContent(); - List animalsDto = this.mapper.map(animals); - return new PageImpl<>(animalsDto); - } - - @Override - public AnimalDto findAnimal(String id) { - AnimalEntity animal = this.animalRepository.findById(Long.valueOf(id)).get(); - if (animal != null) { - return this.mapper.map(animal); - } else { - return null; - } - } - - @Override - public AnimalDto findAnimalByName(String name) { - AnimalEntity animal = this.animalRepository.findByName(name); - if (animal != null) { - return this.mapper.map(animal); - } else { - return null; - } - } - -} diff --git a/src/main/java/com/devonfw/demoquarkus/logic/UcManageAnimal.java b/src/main/java/com/devonfw/demoquarkus/logic/UcManageAnimal.java deleted file mode 100644 index 0e65dc64..00000000 --- a/src/main/java/com/devonfw/demoquarkus/logic/UcManageAnimal.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.devonfw.demoquarkus.logic; - -import com.devonfw.demoquarkus.service.v1.model.AnimalDto; -import com.devonfw.demoquarkus.service.v1.model.NewAnimalDto; - -public interface UcManageAnimal { - AnimalDto saveAnimal(NewAnimalDto dto); - - AnimalDto deleteAnimal(String id); -} diff --git a/src/main/java/com/devonfw/demoquarkus/logic/UcManageAnimalImpl.java b/src/main/java/com/devonfw/demoquarkus/logic/UcManageAnimalImpl.java deleted file mode 100644 index 38047b1c..00000000 --- a/src/main/java/com/devonfw/demoquarkus/logic/UcManageAnimalImpl.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.devonfw.demoquarkus.logic; - -import com.devonfw.demoquarkus.domain.model.AnimalEntity; -import com.devonfw.demoquarkus.domain.repo.AnimalRepository; -import com.devonfw.demoquarkus.service.v1.mapper.AnimalMapper; -import com.devonfw.demoquarkus.service.v1.model.AnimalDto; -import com.devonfw.demoquarkus.service.v1.model.NewAnimalDto; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.transaction.Transactional; - -@Named -@Transactional -public class UcManageAnimalImpl implements UcManageAnimal { - @Inject - AnimalRepository animalRepository; - - @Inject - AnimalMapper mapper; - - @Override - public AnimalDto saveAnimal(NewAnimalDto dto) { - AnimalEntity created = this.animalRepository.save(this.mapper.create(dto)); - return this.mapper.map(created); - } - - @Override - public AnimalDto deleteAnimal(String id) { - AnimalEntity animal = this.animalRepository.findById(Long.valueOf(id)).get(); - if (animal != null) { - this.animalRepository.delete(animal); - return this.mapper.map(animal); - } else { - return null; - } - } -} diff --git a/src/main/java/com/devonfw/demoquarkus/service/v1/AnimalRestService.java b/src/main/java/com/devonfw/demoquarkus/service/v1/AnimalRestService.java deleted file mode 100644 index 350bb165..00000000 --- a/src/main/java/com/devonfw/demoquarkus/service/v1/AnimalRestService.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.devonfw.demoquarkus.service.v1; - -import javax.inject.Inject; -import javax.ws.rs.BeanParam; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.UriInfo; - -import com.devonfw.demoquarkus.logic.UcFindAnimal; -import com.devonfw.demoquarkus.logic.UcManageAnimal; -import com.devonfw.demoquarkus.service.v1.model.AnimalDto; -import com.devonfw.demoquarkus.service.v1.model.AnimalSearchCriteriaDto; -import com.devonfw.demoquarkus.service.v1.model.NewAnimalDto; -import org.eclipse.microprofile.openapi.annotations.Operation; -import org.eclipse.microprofile.openapi.annotations.media.Content; -import org.eclipse.microprofile.openapi.annotations.media.Schema; -import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; -import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; -import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; -import org.springframework.data.domain.PageImpl; -import org.tkit.quarkus.rs.models.PageResultDTO; - -//In Quarkus all JAX-RS resources are treated as CDI beans -//default is Singleton scope -@Path("/animals") -// how we serialize response -@Produces(MediaType.APPLICATION_JSON) -// how we deserialize params -@Consumes(MediaType.APPLICATION_JSON) -public class AnimalRestService { - - // using @Context we can inject contextual info from JAXRS(e.g. http request, current uri info, endpoint info...) - @Context - UriInfo uriInfo; - - @Inject - UcFindAnimal ucFindAnimal; - - @Inject - UcManageAnimal ucManageAnimal; - - @APIResponses({ - @APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = PagedAnimalResponse.class))), - @APIResponse(responseCode = "500")}) - @Operation(operationId = "Get Animals", description = "Returns list of animals matching given criteria, uses pagination") - @GET - // REST service methods should not declare exceptions, any thrown error will be transformed by exceptionMapper in - // tkit-rest - // We did not define custom @Path - so it will use class level path - public PageImpl getAll(@BeanParam AnimalSearchCriteriaDto dto) { - return (PageImpl) ucFindAnimal.findAnimals(dto); - } - - @GET - @Path("criteriaApi") - public PageImpl getAllCriteriaApi(@BeanParam AnimalSearchCriteriaDto dto) { - return (PageImpl) ucFindAnimal.findAnimalsByCriteriaApi(dto); - } - - @GET - @Path("queryDsl") - public PageImpl getAllQueryDsl(@BeanParam AnimalSearchCriteriaDto dto) { - return (PageImpl) ucFindAnimal.findAnimalsByQueryDsl(dto); - } - - @GET - @Path("query") - public PageImpl getAllQuery(@BeanParam AnimalSearchCriteriaDto dto) { - return (PageImpl) ucFindAnimal.findAnimalsByNameQuery(dto); - } - - @GET - @Path("nativeQuery") - public PageImpl getAllNativeQuery(@BeanParam AnimalSearchCriteriaDto dto) { - return (PageImpl) ucFindAnimal.findAnimalsByNameNativeQuery(dto); - } - - @GET - @Path("ordered") - public PageImpl getAllOrderedByName() { - return (PageImpl) ucFindAnimal.findAnimalsOrderedByName(); - } - - @APIResponses({ - @APIResponse(responseCode = "201", description = "OK, New animal created", content = @Content(schema = @Schema(implementation = NewAnimalDto.class))), - @APIResponse(responseCode = "400", description = "Client side error, invalid request"), - @APIResponse(responseCode = "500")}) - @Operation(operationId = "createNewAnimal", description = "Stores new animal in DB") - @POST - // We did not define custom @Path - so it will use class level path. - // Although we now have 2 methods with same path, it is ok, because it is a different method (get vs post) - public AnimalDto createNewAnimal(NewAnimalDto dto) { - return ucManageAnimal.saveAnimal(dto); - } - - @APIResponses({ - @APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AnimalDto.class))), - @APIResponse(responseCode = "404", description = "Animal not found"), @APIResponse(responseCode = "500")}) - @Operation(operationId = "getAnimalById", description = "Returns animal with given id") - @GET - @Path("{id}") - public AnimalDto getAnimalById(@Parameter(description = "Animal unique id") @PathParam("id") String id) { - return ucFindAnimal.findAnimal(id); - } - - @GET - @Path("name/{name}") - public AnimalDto getAnimalByName(@PathParam("name") String name) { - return ucFindAnimal.findAnimalByName(name); - } - - @APIResponses({ - @APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AnimalDto.class))), - @APIResponse(responseCode = "404", description = "Animal not found"), @APIResponse(responseCode = "500")}) - @Operation(operationId = "deleteAnimalById", description = "Deletes the animal with given id") - @DELETE - @Path("{id}") - public AnimalDto deleteAnimalById(@Parameter(description = "Animal unique id") @PathParam("id") String id) { - return ucManageAnimal.deleteAnimal(id); - } - - private static class PagedAnimalResponse extends PageResultDTO { - } - -} diff --git a/src/main/java/com/devonfw/demoquarkus/service/v1/mapper/AnimalMapper.java b/src/main/java/com/devonfw/demoquarkus/service/v1/mapper/AnimalMapper.java deleted file mode 100644 index 32e9a02b..00000000 --- a/src/main/java/com/devonfw/demoquarkus/service/v1/mapper/AnimalMapper.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.devonfw.demoquarkus.service.v1.mapper; - -import java.util.List; - -import org.mapstruct.Mapper; -import org.tkit.quarkus.rs.mappers.OffsetDateTimeMapper; - -import com.devonfw.demoquarkus.domain.model.AnimalEntity; -import com.devonfw.demoquarkus.service.v1.model.AnimalDto; -import com.devonfw.demoquarkus.service.v1.model.NewAnimalDto; - -//mapstruct will generate an impl class(CDI bean, see pom.xml) from this interface at compile time -@Mapper(uses = OffsetDateTimeMapper.class) -public interface AnimalMapper { - - AnimalDto map(AnimalEntity model); - - AnimalEntity create(NewAnimalDto dto); - - List map(List animals); -} diff --git a/src/main/java/com/devonfw/demoquarkus/service/v1/model/AnimalDto.java b/src/main/java/com/devonfw/demoquarkus/service/v1/model/AnimalDto.java deleted file mode 100644 index dffa5b28..00000000 --- a/src/main/java/com/devonfw/demoquarkus/service/v1/model/AnimalDto.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.devonfw.demoquarkus.service.v1.model; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AnimalDto { - - private Long id; - - private String name; - - private String basicInfo; - - private int numberOfLegs; - -} diff --git a/src/main/java/com/devonfw/demoquarkus/service/v1/model/AnimalSearchCriteriaDto.java b/src/main/java/com/devonfw/demoquarkus/service/v1/model/AnimalSearchCriteriaDto.java deleted file mode 100644 index eb9868bb..00000000 --- a/src/main/java/com/devonfw/demoquarkus/service/v1/model/AnimalSearchCriteriaDto.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.devonfw.demoquarkus.service.v1.model; - -import javax.ws.rs.DefaultValue; -import javax.ws.rs.QueryParam; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AnimalSearchCriteriaDto { - - @QueryParam("name") - private String name; - - @QueryParam("legs") - private Integer numberOfLegs; - - @QueryParam("page") - @DefaultValue("0") - private int pageNumber = 0; - - @QueryParam("size") - @DefaultValue("10") - private int pageSize = 10; -} diff --git a/src/main/java/com/devonfw/demoquarkus/service/v1/model/NewAnimalDto.java b/src/main/java/com/devonfw/demoquarkus/service/v1/model/NewAnimalDto.java deleted file mode 100644 index 2075515d..00000000 --- a/src/main/java/com/devonfw/demoquarkus/service/v1/model/NewAnimalDto.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.devonfw.demoquarkus.service.v1.model; - -import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; -import org.eclipse.microprofile.openapi.annotations.media.Schema; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class NewAnimalDto { - - @Schema(nullable = false, description = "Animal name", minLength = 3, maxLength = 50) - private String name; - - @Schema(description = "Animal tag line", minLength = 3, maxLength = 50) - private String basicInfo; - - @Schema(type = SchemaType.INTEGER, description = "How many legs does the animal have", example = "3") - private int numberOfLegs; - -} diff --git a/src/main/java/com/devonfw/demoquarkus/DemoApplication.java b/src/main/java/com/devonfw/quarkus/DemoApplication.java similarity index 70% rename from src/main/java/com/devonfw/demoquarkus/DemoApplication.java rename to src/main/java/com/devonfw/quarkus/DemoApplication.java index 4a5bd688..1c0f6662 100644 --- a/src/main/java/com/devonfw/demoquarkus/DemoApplication.java +++ b/src/main/java/com/devonfw/quarkus/DemoApplication.java @@ -1,4 +1,4 @@ -package com.devonfw.demoquarkus; +package com.devonfw.quarkus; import javax.ws.rs.core.Application; @@ -8,7 +8,7 @@ import org.eclipse.microprofile.openapi.annotations.tags.Tag; @OpenAPIDefinition(tags = { -@Tag(name = "animal", description = "Animal API.") }, info = @Info(title = "My Demo App RESTful API", version = "1.0.0", contact = @Contact(name = "API Support", email = "support@acme.com"))) +@Tag(name = "product", description = "Product API.") }, info = @Info(title = "My Demo App RESTful API", version = "1.0.0", contact = @Contact(name = "API Support", email = "support@acme.com"))) /** * JaxRS application is not required in quarkus, but it is useful to place central API docs somewhere. We could also use * package-info.java for this. diff --git a/src/main/java/com/devonfw/quarkus/general/domain/model/ApplicationPersistenceEntity.java b/src/main/java/com/devonfw/quarkus/general/domain/model/ApplicationPersistenceEntity.java new file mode 100644 index 00000000..f3f9d079 --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/general/domain/model/ApplicationPersistenceEntity.java @@ -0,0 +1,79 @@ +package com.devonfw.quarkus.general.domain.model; + +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; +import javax.persistence.Transient; +import javax.persistence.Version; + +/** + * Abstract base class for all {@link PersistenceEntity persistence entities} with an {@link #getId() id} and a + * {@link #getModificationCounter() modificationCounter} (version) field. All persistence entities of this application + * should inherit from this class. It is using JPA annotations at the getters what has several advantages but also + * implies that you have to annotate transient getter methods with the {@link Transient} annotation. + */ +@MappedSuperclass +public abstract class ApplicationPersistenceEntity { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private Integer modificationCounter; + + /** + * The constructor. + */ + public ApplicationPersistenceEntity() { + + super(); + } + + public Long getId() { + + return this.id; + } + + public void setId(Long id) { + + this.id = id; + } + + @Version + public Integer getModificationCounter() { + + return this.modificationCounter; + } + + public void setModificationCounter(Integer version) { + + this.modificationCounter = version; + } + + @Override + public String toString() { + + StringBuilder buffer = new StringBuilder(); + toString(buffer); + return buffer.toString(); + } + + /** + * Method to extend {@link #toString()} logic. + * + * @param buffer is the {@link StringBuilder} where to {@link StringBuilder#append(Object) append} the string + * representation. + */ + protected void toString(StringBuilder buffer) { + + buffer.append(getClass().getSimpleName()); + if (this.id != null) { + buffer.append("[id="); + buffer.append(this.id); + buffer.append("]"); + } + } +} diff --git a/src/main/java/com/devonfw/quarkus/productmanagement/domain/model/ProductEntity.java b/src/main/java/com/devonfw/quarkus/productmanagement/domain/model/ProductEntity.java new file mode 100644 index 00000000..b7b22ac0 --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/productmanagement/domain/model/ProductEntity.java @@ -0,0 +1,26 @@ +package com.devonfw.quarkus.productmanagement.domain.model; + +import java.math.BigDecimal; + +import javax.persistence.Entity; +import javax.persistence.Table; + +import com.devonfw.quarkus.general.domain.model.ApplicationPersistenceEntity; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "Product") +// A JPA entity requires at least 2 things @Entity annotation and an ID +// by default, the DB table will have the same name as our class +public class ProductEntity extends ApplicationPersistenceEntity { + + private String title; + + private String description; + + private BigDecimal price; +} diff --git a/src/main/java/com/devonfw/quarkus/productmanagement/domain/model/ProductSearchCriteria.java b/src/main/java/com/devonfw/quarkus/productmanagement/domain/model/ProductSearchCriteria.java new file mode 100644 index 00000000..4cca919b --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/productmanagement/domain/model/ProductSearchCriteria.java @@ -0,0 +1,19 @@ +package com.devonfw.quarkus.productmanagement.domain.model; + +import java.math.BigDecimal; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ProductSearchCriteria { + + private String title; + + private Integer pageNumber; + + private Integer pageSize; + + private BigDecimal price, priceMin, priceMax; +} diff --git a/src/main/java/com/devonfw/quarkus/productmanagement/domain/repo/ProductFragment.java b/src/main/java/com/devonfw/quarkus/productmanagement/domain/repo/ProductFragment.java new file mode 100644 index 00000000..576fba29 --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/productmanagement/domain/repo/ProductFragment.java @@ -0,0 +1,16 @@ +package com.devonfw.quarkus.productmanagement.domain.repo; + +import org.springframework.data.domain.Page; + +import com.devonfw.quarkus.productmanagement.domain.model.ProductEntity; +import com.devonfw.quarkus.productmanagement.service.v1.model.ProductSearchCriteriaDto; + +public interface ProductFragment { + public Page findAllCriteriaApi(ProductSearchCriteriaDto dto); + + public Page findAllQueryDsl(ProductSearchCriteriaDto dto); + + public Page findByTitleNativeQuery(ProductSearchCriteriaDto dto); + + public Page findByTitleQuery(ProductSearchCriteriaDto dto); +} diff --git a/src/main/java/com/devonfw/quarkus/productmanagement/domain/repo/ProductFragmentImpl.java b/src/main/java/com/devonfw/quarkus/productmanagement/domain/repo/ProductFragmentImpl.java new file mode 100644 index 00000000..3c7e0c78 --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/productmanagement/domain/repo/ProductFragmentImpl.java @@ -0,0 +1,114 @@ +package com.devonfw.quarkus.productmanagement.domain.repo; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; +import javax.persistence.EntityManager; +import javax.persistence.Query; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; + +import com.devonfw.quarkus.productmanagement.domain.model.ProductEntity; +import com.devonfw.quarkus.productmanagement.domain.model.ProductEntity_; +import com.devonfw.quarkus.productmanagement.domain.model.QProductEntity; +import com.devonfw.quarkus.productmanagement.service.v1.model.ProductSearchCriteriaDto; +import com.querydsl.jpa.impl.JPAQuery; + +public class ProductFragmentImpl implements ProductFragment { + + @Inject + EntityManager em; + + @Override + public Page findAllCriteriaApi(ProductSearchCriteriaDto dto) { + + CriteriaQuery cq = this.em.getCriteriaBuilder().createQuery(ProductEntity.class); + Root root = cq.from(ProductEntity.class); + List predicates = new ArrayList<>(); + CriteriaBuilder cb = this.em.getCriteriaBuilder(); + if (dto.getTitle() != null && !dto.getTitle().isEmpty()) { + predicates.add(cb.like(root.get(ProductEntity_.TITLE), dto.getTitle())); + } + + if (dto.getPrice() != null) { + predicates.add(cb.gt(root.get(ProductEntity_.PRICE), dto.getPrice())); + } else if (dto.getPriceMin() != null || dto.getPriceMax() != null) { + if (dto.getPriceMin() != null) { + predicates.add(cb.gt(root.get(ProductEntity_.PRICE), dto.getPriceMin())); + } + if (dto.getPriceMax() != null) { + predicates.add(cb.lt(root.get(ProductEntity_.PRICE), dto.getPriceMax())); + } + } + + if (!predicates.isEmpty()) + + { + cq.where(predicates.toArray(new Predicate[0])); + } + + // Order by title + cq.orderBy(cb.desc(root.get(ProductEntity_.TITLE))); + + TypedQuery products = this.em.createQuery(cq).setFirstResult(dto.getPageNumber() * dto.getPageSize()) + .setMaxResults(dto.getPageSize()); + return new PageImpl(products.getResultList(), PageRequest.of(dto.getPageNumber(), dto.getPageSize()), + products.getResultList().size()); + } + + @Override + public Page findAllQueryDsl(ProductSearchCriteriaDto dto) { + + QProductEntity product = QProductEntity.productEntity; + JPAQuery query = new JPAQuery(this.em); + query.from(product); + if (dto.getTitle() != null && !dto.getTitle().isEmpty()) { + query.where(product.title.eq(dto.getTitle())); + } + + if (dto.getPrice() != null) { + query.where(product.price.eq(dto.getPrice())); + } else if (dto.getPriceMin() != null || dto.getPriceMax() != null) { + if (dto.getPriceMin() != null) { + query.where(product.price.gt(dto.getPriceMin())); + } + if (dto.getPriceMax() != null) { + query.where(product.price.lt(dto.getPriceMax())); + } + } + + // Order by title + query.orderBy(product.title.desc()); + + List products = query.limit(dto.getPageSize()).offset(dto.getPageNumber() * dto.getPageSize()) + .fetch(); + return new PageImpl<>(products, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), products.size()); + } + + @Override + public Page findByTitleQuery(ProductSearchCriteriaDto dto) { + + Query query = this.em.createQuery("select a from ProductEntity a where a.title = :title"); + query.setParameter("title", dto.getTitle()); + List products = query.getResultList(); + return new PageImpl<>(products, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), products.size()); + } + + @Override + public Page findByTitleNativeQuery(ProductSearchCriteriaDto dto) { + + Query query = this.em.createNativeQuery("select * from ProductEntity where title = :title", ProductEntity.class); + query.setParameter("title", dto.getTitle()); + List products = query.getResultList(); + return new PageImpl<>(products, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), products.size()); + } + +} diff --git a/src/main/java/com/devonfw/quarkus/productmanagement/domain/repo/ProductRepository.java b/src/main/java/com/devonfw/quarkus/productmanagement/domain/repo/ProductRepository.java new file mode 100644 index 00000000..39a68d4f --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/productmanagement/domain/repo/ProductRepository.java @@ -0,0 +1,16 @@ +package com.devonfw.quarkus.productmanagement.domain.repo; + +import org.springframework.data.domain.Page; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; + +import com.devonfw.quarkus.productmanagement.domain.model.ProductEntity; + +public interface ProductRepository extends CrudRepository, ProductFragment { + + @Query("select a from ProductEntity a where title = :title") + ProductEntity findByTitle(@Param("title") String title); + + Page findAllByOrderByTitle(); +} \ No newline at end of file diff --git a/src/main/java/com/devonfw/quarkus/productmanagement/logic/UcFindProduct.java b/src/main/java/com/devonfw/quarkus/productmanagement/logic/UcFindProduct.java new file mode 100644 index 00000000..487232d8 --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/productmanagement/logic/UcFindProduct.java @@ -0,0 +1,24 @@ +package com.devonfw.quarkus.productmanagement.logic; + +import org.springframework.data.domain.Page; + +import com.devonfw.quarkus.productmanagement.service.v1.model.ProductDto; +import com.devonfw.quarkus.productmanagement.service.v1.model.ProductSearchCriteriaDto; + +public interface UcFindProduct { + Page findProducts(ProductSearchCriteriaDto dto); + + Page findProductsByCriteriaApi(ProductSearchCriteriaDto dto); + + Page findProductsByQueryDsl(ProductSearchCriteriaDto dto); + + Page findProductsByTitleQuery(ProductSearchCriteriaDto dto); + + Page findProductsByTitleNativeQuery(ProductSearchCriteriaDto dto); + + Page findProductsOrderedByTitle(); + + ProductDto findProduct(String id); + + ProductDto findProductByTitle(String title); +} diff --git a/src/main/java/com/devonfw/quarkus/productmanagement/logic/UcFindProductImpl.java b/src/main/java/com/devonfw/quarkus/productmanagement/logic/UcFindProductImpl.java new file mode 100644 index 00000000..759f7dc0 --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/productmanagement/logic/UcFindProductImpl.java @@ -0,0 +1,103 @@ +package com.devonfw.quarkus.productmanagement.logic; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.transaction.Transactional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; + +import com.devonfw.quarkus.productmanagement.domain.model.ProductEntity; +import com.devonfw.quarkus.productmanagement.domain.repo.ProductRepository; +import com.devonfw.quarkus.productmanagement.service.v1.mapper.ProductMapper; +import com.devonfw.quarkus.productmanagement.service.v1.model.ProductDto; +import com.devonfw.quarkus.productmanagement.service.v1.model.ProductSearchCriteriaDto; + +import lombok.extern.slf4j.Slf4j; + +@Named +@Transactional +@Slf4j +public class UcFindProductImpl implements UcFindProduct { + @Inject + ProductRepository ProductRepository; + + @Inject + ProductMapper mapper; + + @Override + public Page findProducts(ProductSearchCriteriaDto dto) { + + Iterable productsIterator = this.ProductRepository.findAll(); + List products = new ArrayList(); + productsIterator.forEach(products::add); + List productsDto = this.mapper.map(products); + return new PageImpl<>(productsDto, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), productsDto.size()); + } + + @Override + public Page findProductsByCriteriaApi(ProductSearchCriteriaDto dto) { + + List products = this.ProductRepository.findAllCriteriaApi(dto).getContent(); + List productsDto = this.mapper.map(products); + return new PageImpl<>(productsDto, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), productsDto.size()); + } + + @Override + public Page findProductsByQueryDsl(ProductSearchCriteriaDto dto) { + + List products = this.ProductRepository.findAllQueryDsl(dto).getContent(); + List productsDto = this.mapper.map(products); + return new PageImpl<>(productsDto, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), productsDto.size()); + } + + @Override + public Page findProductsByTitleQuery(ProductSearchCriteriaDto dto) { + + List products = this.ProductRepository.findByTitleQuery(dto).getContent(); + List productsDto = this.mapper.map(products); + return new PageImpl<>(productsDto, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), productsDto.size()); + } + + @Override + public Page findProductsByTitleNativeQuery(ProductSearchCriteriaDto dto) { + + List products = this.ProductRepository.findByTitleNativeQuery(dto).getContent(); + List productsDto = this.mapper.map(products); + return new PageImpl<>(productsDto, PageRequest.of(dto.getPageNumber(), dto.getPageSize()), productsDto.size()); + } + + @Override + public Page findProductsOrderedByTitle() { + + List products = this.ProductRepository.findAllByOrderByTitle().getContent(); + List productsDto = this.mapper.map(products); + return new PageImpl<>(productsDto); + } + + @Override + public ProductDto findProduct(String id) { + + ProductEntity product = this.ProductRepository.findById(Long.valueOf(id)).get(); + if (product != null) { + return this.mapper.map(product); + } else { + return null; + } + } + + @Override + public ProductDto findProductByTitle(String title) { + + ProductEntity product = this.ProductRepository.findByTitle(title); + if (product != null) { + return this.mapper.map(product); + } else { + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/devonfw/quarkus/productmanagement/logic/UcManageProduct.java b/src/main/java/com/devonfw/quarkus/productmanagement/logic/UcManageProduct.java new file mode 100644 index 00000000..1b8cf630 --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/productmanagement/logic/UcManageProduct.java @@ -0,0 +1,10 @@ +package com.devonfw.quarkus.productmanagement.logic; + +import com.devonfw.quarkus.productmanagement.service.v1.model.NewProductDto; +import com.devonfw.quarkus.productmanagement.service.v1.model.ProductDto; + +public interface UcManageProduct { + ProductDto saveProduct(NewProductDto dto); + + ProductDto deleteProduct(String id); +} diff --git a/src/main/java/com/devonfw/quarkus/productmanagement/logic/UcManageProductImpl.java b/src/main/java/com/devonfw/quarkus/productmanagement/logic/UcManageProductImpl.java new file mode 100644 index 00000000..d79477a3 --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/productmanagement/logic/UcManageProductImpl.java @@ -0,0 +1,40 @@ +package com.devonfw.quarkus.productmanagement.logic; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.transaction.Transactional; + +import com.devonfw.quarkus.productmanagement.domain.model.ProductEntity; +import com.devonfw.quarkus.productmanagement.domain.repo.ProductRepository; +import com.devonfw.quarkus.productmanagement.service.v1.mapper.ProductMapper; +import com.devonfw.quarkus.productmanagement.service.v1.model.NewProductDto; +import com.devonfw.quarkus.productmanagement.service.v1.model.ProductDto; + +@Named +@Transactional +public class UcManageProductImpl implements UcManageProduct { + @Inject + ProductRepository productRepository; + + @Inject + ProductMapper mapper; + + @Override + public ProductDto saveProduct(NewProductDto dto) { + + ProductEntity created = this.productRepository.save(this.mapper.create(dto)); + return this.mapper.map(created); + } + + @Override + public ProductDto deleteProduct(String id) { + + ProductEntity product = this.productRepository.findById(Long.valueOf(id)).get(); + if (product != null) { + this.productRepository.delete(product); + return this.mapper.map(product); + } else { + return null; + } + } +} diff --git a/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/ProductRestService.java b/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/ProductRestService.java new file mode 100644 index 00000000..b886fcea --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/ProductRestService.java @@ -0,0 +1,143 @@ +package com.devonfw.quarkus.productmanagement.service.v1; + +import javax.inject.Inject; +import javax.ws.rs.BeanParam; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; +import org.springframework.data.domain.PageImpl; +import org.tkit.quarkus.rs.models.PageResultDTO; + +import com.devonfw.quarkus.productmanagement.logic.UcFindProduct; +import com.devonfw.quarkus.productmanagement.logic.UcManageProduct; +import com.devonfw.quarkus.productmanagement.service.v1.model.NewProductDto; +import com.devonfw.quarkus.productmanagement.service.v1.model.ProductDto; +import com.devonfw.quarkus.productmanagement.service.v1.model.ProductSearchCriteriaDto; + +//In Quarkus all JAX-RS resources are treated as CDI beans +//default is Singleton scope +@Path("/products") +// how we serialize response +@Produces(MediaType.APPLICATION_JSON) +// how we deserialize params +@Consumes(MediaType.APPLICATION_JSON) +public class ProductRestService { + + // using @Context we can inject contextual info from JAXRS(e.g. http request, current uri info, endpoint info...) + @Context + UriInfo uriInfo; + + @Inject + UcFindProduct ucFindProduct; + + @Inject + UcManageProduct ucManageProduct; + + @APIResponses({ + @APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = PagedProductResponse.class))), + @APIResponse(responseCode = "500") }) + @Operation(operationId = "Get Products", description = "Returns list of Products matching given criteria, uses pagination") + @GET + // REST service methods should not declare exceptions, any thrown error will be transformed by exceptionMapper in + // tkit-rest + // We did not define custom @Path - so it will use class level path + public PageImpl getAll(@BeanParam ProductSearchCriteriaDto dto) { + + return (PageImpl) this.ucFindProduct.findProducts(dto); + } + + @GET + @Path("criteriaApi") + public PageImpl getAllCriteriaApi(@BeanParam ProductSearchCriteriaDto dto) { + + return (PageImpl) this.ucFindProduct.findProductsByCriteriaApi(dto); + } + + @GET + @Path("queryDsl") + public PageImpl getAllQueryDsl(@BeanParam ProductSearchCriteriaDto dto) { + + return (PageImpl) this.ucFindProduct.findProductsByQueryDsl(dto); + } + + @GET + @Path("query") + public PageImpl getAllQuery(@BeanParam ProductSearchCriteriaDto dto) { + + return (PageImpl) this.ucFindProduct.findProductsByTitleQuery(dto); + } + + @GET + @Path("nativeQuery") + public PageImpl getAllNativeQuery(@BeanParam ProductSearchCriteriaDto dto) { + + return (PageImpl) this.ucFindProduct.findProductsByTitleNativeQuery(dto); + } + + @GET + @Path("ordered") + public PageImpl getAllOrderedByTitle() { + + return (PageImpl) this.ucFindProduct.findProductsOrderedByTitle(); + } + + @APIResponses({ + @APIResponse(responseCode = "201", description = "OK, New Product created", content = @Content(schema = @Schema(implementation = NewProductDto.class))), + @APIResponse(responseCode = "400", description = "Client side error, invalid request"), + @APIResponse(responseCode = "500") }) + @Operation(operationId = "createNewProduct", description = "Stores new Product in DB") + @POST + // We did not define custom @Path - so it will use class level path. + // Although we now have 2 methods with same path, it is ok, because it is a different method (get vs post) + public ProductDto createNewProduct(NewProductDto dto) { + + return this.ucManageProduct.saveProduct(dto); + } + + @APIResponses({ + @APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ProductDto.class))), + @APIResponse(responseCode = "404", description = "Product not found"), @APIResponse(responseCode = "500") }) + @Operation(operationId = "getProductById", description = "Returns Product with given id") + @GET + @Path("{id}") + public ProductDto getProductById(@Parameter(description = "Product unique id") @PathParam("id") String id) { + + return this.ucFindProduct.findProduct(id); + } + + @GET + @Path("title/{title}") + public ProductDto getProductByTitle(@PathParam("title") String title) { + + return this.ucFindProduct.findProductByTitle(title); + } + + @APIResponses({ + @APIResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ProductDto.class))), + @APIResponse(responseCode = "404", description = "Product not found"), @APIResponse(responseCode = "500") }) + @Operation(operationId = "deleteProductById", description = "Deletes the Product with given id") + @DELETE + @Path("{id}") + public ProductDto deleteProductById(@Parameter(description = "Product unique id") @PathParam("id") String id) { + + return this.ucManageProduct.deleteProduct(id); + } + + private static class PagedProductResponse extends PageResultDTO { + } + +} diff --git a/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/mapper/ProductMapper.java b/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/mapper/ProductMapper.java new file mode 100644 index 00000000..46c1f856 --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/mapper/ProductMapper.java @@ -0,0 +1,21 @@ +package com.devonfw.quarkus.productmanagement.service.v1.mapper; + +import java.util.List; + +import org.mapstruct.Mapper; +import org.tkit.quarkus.rs.mappers.OffsetDateTimeMapper; + +import com.devonfw.quarkus.productmanagement.domain.model.ProductEntity; +import com.devonfw.quarkus.productmanagement.service.v1.model.NewProductDto; +import com.devonfw.quarkus.productmanagement.service.v1.model.ProductDto; + +//mapstruct will generate an impl class(CDI bean, see pom.xml) from this interface at compile time +@Mapper(uses = OffsetDateTimeMapper.class) +public interface ProductMapper { + + ProductDto map(ProductEntity model); + + ProductEntity create(NewProductDto dto); + + List map(List Products); +} diff --git a/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/model/NewProductDto.java b/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/model/NewProductDto.java new file mode 100644 index 00000000..69cf8119 --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/model/NewProductDto.java @@ -0,0 +1,23 @@ +package com.devonfw.quarkus.productmanagement.service.v1.model; + +import java.math.BigDecimal; + +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NewProductDto { + + @Schema(nullable = false, description = "Product title", minLength = 3, maxLength = 500) + private String title; + + @Schema(description = "Product description", minLength = 3, maxLength = 500) + private String description; + + @Schema(description = "Product price") + private BigDecimal price; + +} diff --git a/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/model/ProductDto.java b/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/model/ProductDto.java new file mode 100644 index 00000000..c3dd0465 --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/model/ProductDto.java @@ -0,0 +1,20 @@ +package com.devonfw.quarkus.productmanagement.service.v1.model; + +import java.math.BigDecimal; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ProductDto { + + private Long id; + + private String title; + + private String description; + + private BigDecimal price; + +} diff --git a/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/model/ProductSearchCriteriaDto.java b/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/model/ProductSearchCriteriaDto.java new file mode 100644 index 00000000..1ad69ed5 --- /dev/null +++ b/src/main/java/com/devonfw/quarkus/productmanagement/service/v1/model/ProductSearchCriteriaDto.java @@ -0,0 +1,34 @@ +package com.devonfw.quarkus.productmanagement.service.v1.model; + +import java.math.BigDecimal; + +import javax.ws.rs.DefaultValue; +import javax.ws.rs.QueryParam; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ProductSearchCriteriaDto { + + @QueryParam("title") + private String title; + + @QueryParam("page") + @DefaultValue("0") + private int pageNumber = 0; + + @QueryParam("size") + @DefaultValue("10") + private int pageSize = 10; + + @QueryParam("priceMin") + private BigDecimal priceMin; + + @QueryParam("priceMax") + private BigDecimal priceMax; + + @QueryParam("price") + private BigDecimal price; +} diff --git a/src/test/java/com/devonfw/demoquarkus/service/v1/AnimalRestServiceTest.java b/src/test/java/com/devonfw/demoquarkus/service/v1/ProductRestServiceTest.java similarity index 58% rename from src/test/java/com/devonfw/demoquarkus/service/v1/AnimalRestServiceTest.java rename to src/test/java/com/devonfw/demoquarkus/service/v1/ProductRestServiceTest.java index 30f6e853..a67186c9 100644 --- a/src/test/java/com/devonfw/demoquarkus/service/v1/AnimalRestServiceTest.java +++ b/src/test/java/com/devonfw/demoquarkus/service/v1/ProductRestServiceTest.java @@ -6,14 +6,17 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.math.BigDecimal; + import javax.ws.rs.core.MediaType; -import com.devonfw.demoquarkus.service.v1.model.AnimalDto; import org.junit.jupiter.api.Test; import org.tkit.quarkus.rs.models.PageResultDTO; import org.tkit.quarkus.test.WithDBData; import org.tkit.quarkus.test.docker.DockerComposeTestResource; +import com.devonfw.quarkus.productmanagement.service.v1.model.ProductDto; + import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; import io.restassured.common.mapper.TypeRef; @@ -23,75 +26,74 @@ //we get a real postgresdb for our tests which will be stopped after tests. No manual test setup is needed. @QuarkusTest @QuarkusTestResource(DockerComposeTestResource.class) -class AnimalRestServiceTest {// extends AbstractTest { +class ProductRestServiceTest {// extends AbstractTest { @Test // we also started a micro container, that can populated DB with data from excel // annotating class or method with @WithDBData allows us to scope data for each test even if we use the same DB - @WithDBData(value = "data/animal.xls", deleteBeforeInsert = true) + @WithDBData(value = "data/product.xls", deleteBeforeInsert = true) void getAll() { - Response response = given().when().contentType(MediaType.APPLICATION_JSON).get("/animals").then().statusCode(200) - // .body("$.size()", equalTo(2)) + Response response = given().when().contentType(MediaType.APPLICATION_JSON).get("/products").then().statusCode(200) .extract().response(); - PageResultDTO animalsReturned = response.as(new TypeRef>() { + PageResultDTO productsReturned = response.as(new TypeRef>() { }); // we import data from /import.sql - ergo expect 1 result - assertEquals(2, animalsReturned.getTotalElements()); + assertEquals(2, productsReturned.getTotalElements()); } @Test void getNonExistingTest() { - Response response = given().when().contentType(MediaType.APPLICATION_JSON).get("/animals/doesnoexist").then().log() + Response response = given().when().contentType(MediaType.APPLICATION_JSON).get("/products/doesnoexist").then().log() .all().statusCode(404).extract().response(); } @Test @WithDBData(value = "data/empty.xls", deleteBeforeInsert = true) - void createNewAnimal() { + void createNewProduct() { - AnimalDto animal = new AnimalDto(); - animal.setName("Dog"); - animal.setBasicInfo("Live"); - animal.setNumberOfLegs(4); + ProductDto product = new ProductDto(); + product.setTitle("HP Notebook"); + product.setDescription("ZBook"); + product.setPrice(BigDecimal.valueOf(1)); - Response response = given().when().body(animal).contentType(MediaType.APPLICATION_JSON).post("/animals").then() + Response response = given().when().body(product).contentType(MediaType.APPLICATION_JSON).post("/products").then() .log().all().statusCode(201).header("Location", notNullValue()).extract().response(); assertEquals(201, response.statusCode()); - response = given().when().contentType(MediaType.APPLICATION_JSON).get("/animals").then().log().all().statusCode(200) - .extract().response(); + response = given().when().contentType(MediaType.APPLICATION_JSON).get("/products").then().log().all() + .statusCode(200).extract().response(); - PageResultDTO animalsReturned = response.as(new TypeRef<>() { + PageResultDTO productsReturned = response.as(new TypeRef<>() { }); - assertEquals(1, animalsReturned.getTotalElements()); - AnimalDto created = animalsReturned.getStream().get(0); + assertEquals(1, productsReturned.getTotalElements()); + ProductDto created = productsReturned.getStream().get(0); assertNotNull(created); - assertEquals(animal.getName(), created.getName()); + assertEquals(product.getTitle(), created.getTitle()); } @Test - @WithDBData(value = "data/animal.xls", deleteBeforeInsert = true) + @WithDBData(value = "data/product.xls", deleteBeforeInsert = true) public void testGetById() { - given().when().log().all().contentType(MediaType.APPLICATION_JSON).get("/animals/1").then().statusCode(200) - .body("basicInfo", equalTo("Cat")); + given().when().log().all().contentType(MediaType.APPLICATION_JSON).get("/products/1").then().statusCode(200) + .body("description", equalTo("Apple Notebook")); } @Test - @WithDBData(value = "data/animal.xls", deleteBeforeInsert = true) + @WithDBData(value = "data/product.xls", deleteBeforeInsert = true) public void deleteById() { // delete - given().when().log().all().contentType(MediaType.APPLICATION_JSON).delete("/animals/1").then().statusCode(200) - .body("name", equalTo("Kitty")); + given().when().log().all().contentType(MediaType.APPLICATION_JSON).delete("/products/1").then().statusCode(200) + .body("title", equalTo("MacBook Pro")); // after deletion it should be deleted - given().when().log().all().contentType(MediaType.APPLICATION_JSON).get("/animals/1").then().statusCode(404); + given().when().log().all().contentType(MediaType.APPLICATION_JSON).get("/products/1").then().statusCode(404); } diff --git a/src/test/resources/data/empty.xls b/src/test/resources/data/empty.xls index 8b6a5a46..1ee268f0 100644 Binary files a/src/test/resources/data/empty.xls and b/src/test/resources/data/empty.xls differ diff --git a/src/test/resources/data/animal.xls b/src/test/resources/data/product.xls similarity index 60% rename from src/test/resources/data/animal.xls rename to src/test/resources/data/product.xls index 8b8fff4b..cbc64915 100644 Binary files a/src/test/resources/data/animal.xls and b/src/test/resources/data/product.xls differ