Skip to content

Commit

Permalink
feat: images Controller (#37)
Browse files Browse the repository at this point in the history
* feat: setup images rest controller

* feat: change theme to product

* feat: size up image length
  • Loading branch information
jsteenke authored Feb 22, 2024
1 parent a4ed256 commit 4912875
Show file tree
Hide file tree
Showing 14 changed files with 804 additions and 2 deletions.
14 changes: 14 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,20 @@
<modelNameSuffix>DTOv1</modelNameSuffix>
</configuration>
</execution>
<execution>
<id>onecx-image-internal</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>src/main/openapi/onecx-image-internal-openapi.yaml</inputSpec>
<apiPackage>gen.org.tkit.onecx.image.rs.internal</apiPackage>
<modelPackage>gen.org.tkit.onecx.image.rs.internal.model</modelPackage>
<generateModelTests>true</generateModelTests>
<generateModelDocumentation>true</generateModelDocumentation>
<typeMappings>File=byte[]</typeMappings>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.tkit.onecx.product.store.domain.daos;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.NoResultException;
import jakarta.transaction.Transactional;

import org.tkit.onecx.product.store.domain.models.*;
import org.tkit.quarkus.jpa.daos.AbstractDAO;
import org.tkit.quarkus.jpa.exceptions.DAOException;

@ApplicationScoped
@Transactional
public class ImageDAO extends AbstractDAO<Image> {

public Image findByRefIdAndRefType(String refId, String refType) {

try {
var cb = this.getEntityManager().getCriteriaBuilder();
var cq = this.criteriaQuery();
var root = cq.from(Image.class);

cq.where(cb.and(cb.equal(root.get(Image_.refId), refId),
cb.equal(root.get(Image_.refType), refType)));

return this.getEntityManager().createQuery(cq).getSingleResult();
} catch (NoResultException nre) {
return null;
} catch (Exception ex) {
throw new DAOException(ImageDAO.ErrorKeys.FIND_ENTITY_BY_REF_ID_REF_TYPE_FAILED, ex);
}
}

@Transactional(value = Transactional.TxType.REQUIRED, rollbackOn = DAOException.class)
public void deleteQueryByRefId(String refId) throws DAOException {
if (refId == null) {
return;
}
try {
var cq = deleteQuery();
var root = cq.from(Image.class);
var cb = this.getEntityManager().getCriteriaBuilder();

cq.where(cb.equal(root.get(Image_.REF_ID), refId));
getEntityManager().createQuery(cq).executeUpdate();
getEntityManager().flush();
} catch (Exception e) {
throw handleConstraint(e, ErrorKeys.FAILED_TO_DELETE_BY_REF_ID_QUERY);
}
}

public enum ErrorKeys {

FAILED_TO_DELETE_BY_REF_ID_QUERY,

FIND_ENTITY_BY_REF_ID_REF_TYPE_FAILED,

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.tkit.onecx.product.store.domain.models;

import jakarta.persistence.*;

import org.tkit.quarkus.jpa.models.TraceableEntity;

import lombok.Getter;
import lombok.Setter;

@Entity
@Getter
@Setter
@Table(name = "IMAGE", uniqueConstraints = {
@UniqueConstraint(name = "IMAGE_CONSTRAINTS", columnNames = { "REF_ID", "REF_TYPE" })
})
@SuppressWarnings("squid:S2160")
public class Image extends TraceableEntity {

@Column(name = "MIME_TYPE")
private String mimeType;

@Column(name = "REF_TYPE")
private String refType;

@Column(name = "REF_ID")
private String refId;

@Column(name = "DATA_LENGTH")
private Integer length;

@Column(name = "DATA")
private byte[] imageData;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package org.tkit.onecx.product.store.rs.internal.controllers;

import static jakarta.transaction.Transactional.TxType.NOT_SUPPORTED;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.validation.ConstraintViolationException;
import jakarta.ws.rs.core.*;

import org.jboss.resteasy.reactive.RestResponse;
import org.jboss.resteasy.reactive.server.ServerExceptionMapper;
import org.tkit.onecx.product.store.domain.daos.ImageDAO;
import org.tkit.onecx.product.store.domain.models.Image;
import org.tkit.onecx.product.store.rs.internal.mappers.ExceptionMapper;
import org.tkit.onecx.product.store.rs.internal.mappers.ImageMapper;
import org.tkit.quarkus.jpa.exceptions.ConstraintException;
import org.tkit.quarkus.log.cdi.LogService;

import gen.org.tkit.onecx.image.rs.internal.ImagesInternalApi;
import gen.org.tkit.onecx.image.rs.internal.model.RefTypeDTO;
import gen.org.tkit.onecx.product.store.rs.internal.model.ProblemDetailResponseDTO;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@LogService
@ApplicationScoped
@Transactional(value = NOT_SUPPORTED)
public class ImagesInternalRestController implements ImagesInternalApi {

@Inject
ExceptionMapper exceptionMapper;

@Inject
ImageDAO imageDAO;

@Context
UriInfo uriInfo;

@Context
HttpHeaders httpHeaders;

@Inject
ImageMapper imageMapper;

@Override
@Transactional
public Response getImage(String refId, RefTypeDTO refType) {
Image image = imageDAO.findByRefIdAndRefType(refId, refType.toString());
if (image == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
return Response.ok(image.getImageData(), image.getMimeType())
.header(HttpHeaders.CONTENT_LENGTH, image.getLength()).build();
}

@Override
public Response updateImage(String refId, RefTypeDTO refType, byte[] body, Integer contentLength) {

Image image = imageDAO.findByRefIdAndRefType(refId, refType.toString());
if (image == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}

var contentType = httpHeaders.getMediaType();
contentType = new MediaType(contentType.getType(), contentType.getSubtype());

image.setLength(contentLength);
image.setMimeType(contentType.toString());
image.setImageData(body);

image = imageDAO.update(image);
return Response.ok(imageMapper.map(image)).build();
}

@Override
public Response uploadImage(Integer contentLength, String refId, RefTypeDTO refType, byte[] body) {

var contentType = httpHeaders.getMediaType();
contentType = new MediaType(contentType.getType(), contentType.getSubtype());
var image = imageMapper.create(refId, refType.toString(), contentType.toString(), contentLength);
image.setLength(contentLength);
image.setImageData(body);
image = imageDAO.create(image);

var imageInfoDTO = imageMapper.map(image);
return Response.created(uriInfo.getAbsolutePathBuilder().path(imageInfoDTO.getId()).build())
.entity(imageInfoDTO)
.build();
}

@ServerExceptionMapper
public RestResponse<ProblemDetailResponseDTO> exception(ConstraintException ex) {
return exceptionMapper.exception(ex);
}

@ServerExceptionMapper
public RestResponse<ProblemDetailResponseDTO> constraint(ConstraintViolationException ex) {
return exceptionMapper.constraint(ex);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package org.tkit.onecx.product.store.rs.internal.mappers;

import java.util.List;
import java.util.Map;
import java.util.Set;

import jakarta.persistence.OptimisticLockException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Path;
import jakarta.ws.rs.core.Response;

import org.jboss.resteasy.reactive.RestResponse;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.tkit.quarkus.jpa.exceptions.ConstraintException;
import org.tkit.quarkus.log.cdi.LogService;
import org.tkit.quarkus.rs.mappers.OffsetDateTimeMapper;

import gen.org.tkit.onecx.product.store.rs.internal.model.ProblemDetailInvalidParamDTO;
import gen.org.tkit.onecx.product.store.rs.internal.model.ProblemDetailParamDTO;
import gen.org.tkit.onecx.product.store.rs.internal.model.ProblemDetailResponseDTO;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Mapper(uses = { OffsetDateTimeMapper.class })
public abstract class ExceptionMapper {

public RestResponse<ProblemDetailResponseDTO> constraint(ConstraintViolationException ex) {
var dto = exception(ErrorKeys.CONSTRAINT_VIOLATIONS.name(), ex.getMessage());
dto.setInvalidParams(createErrorValidationResponse(ex.getConstraintViolations()));
return RestResponse.status(Response.Status.BAD_REQUEST, dto);
}

public RestResponse<ProblemDetailResponseDTO> exception(ConstraintException ex) {
var dto = exception(ex.getMessageKey().name(), ex.getConstraints());
dto.setParams(map(ex.namedParameters));
return RestResponse.status(Response.Status.BAD_REQUEST, dto);
}

@LogService(log = false)
public RestResponse<ProblemDetailResponseDTO> optimisticLock(OptimisticLockException ex) {
var dto = exception(ErrorKeys.OPTIMISTIC_LOCK.name(), ex.getMessage());
return RestResponse.status(Response.Status.BAD_REQUEST, dto);
}

@Mapping(target = "removeParamsItem", ignore = true)
@Mapping(target = "params", ignore = true)
@Mapping(target = "invalidParams", ignore = true)
@Mapping(target = "removeInvalidParamsItem", ignore = true)
public abstract ProblemDetailResponseDTO exception(String errorCode, String detail);

public List<ProblemDetailParamDTO> map(Map<String, Object> params) {
if (params == null) {
return List.of();
}
return params.entrySet().stream().map(e -> {
var item = new ProblemDetailParamDTO();
item.setKey(e.getKey());
if (e.getValue() != null) {
item.setValue(e.getValue().toString());
}
return item;
}).toList();
}

public abstract List<ProblemDetailInvalidParamDTO> createErrorValidationResponse(
Set<ConstraintViolation<?>> constraintViolation);

@Mapping(target = "name", source = "propertyPath")
@Mapping(target = "message", source = "message")
public abstract ProblemDetailInvalidParamDTO createError(ConstraintViolation<?> constraintViolation);

public String mapPath(Path path) {
return path.toString();
}

public enum ErrorKeys {

OPTIMISTIC_LOCK,
CONSTRAINT_VIOLATIONS;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.tkit.onecx.product.store.rs.internal.mappers;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.tkit.onecx.product.store.domain.models.Image;
import org.tkit.quarkus.rs.mappers.OffsetDateTimeMapper;

import gen.org.tkit.onecx.image.rs.internal.model.ImageInfoDTO;

@Mapper(uses = OffsetDateTimeMapper.class)
public interface ImageMapper {

ImageInfoDTO map(Image image);

@Mapping(target = "id", ignore = true)
@Mapping(target = "imageData", ignore = true)
@Mapping(target = "controlTraceabilityManual", ignore = true)
@Mapping(target = "modificationCount", ignore = true)
@Mapping(target = "creationDate", ignore = true)
@Mapping(target = "creationUser", ignore = true)
@Mapping(target = "modificationDate", ignore = true)
@Mapping(target = "modificationUser", ignore = true)
@Mapping(target = "persisted", ignore = true)
@Mapping(target = "refId", source = "refId")
@Mapping(target = "refType", source = "refType")
Image create(String refId, String refType, String mimeType, Integer length);
}
Loading

0 comments on commit 4912875

Please sign in to comment.