Skip to content

Commit

Permalink
feat: migration, add export and import service
Browse files Browse the repository at this point in the history
  • Loading branch information
andrejpetras committed Nov 11, 2023
1 parent 86e46b3 commit f916abb
Show file tree
Hide file tree
Showing 17 changed files with 692 additions and 39 deletions.
12 changes: 12 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,18 @@
<generateApis>false</generateApis>
</configuration>
</execution>
<execution>
<id>exim-v1</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>src/main/openapi/onecx-theme-exim-v1-openapi.yaml</inputSpec>
<apiPackage>gen.io.github.onecx.theme.rs.exim.v1</apiPackage>
<modelPackage>gen.io.github.onecx.theme.rs.exim.v1.model</modelPackage>
<modelNameSuffix>DTOV1</modelNameSuffix>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
Expand Down
25 changes: 24 additions & 1 deletion src/main/java/io/github/onecx/theme/domain/daos/ThemeDAO.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.github.onecx.theme.domain.daos;

import java.util.Set;
import java.util.stream.Stream;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.NoResultException;
import jakarta.transaction.Transactional;
Expand All @@ -11,17 +14,35 @@
import io.github.onecx.theme.domain.models.Theme_;

@ApplicationScoped
@Transactional(Transactional.TxType.NOT_SUPPORTED)
public class ThemeDAO extends AbstractDAO<Theme> {

@Transactional(Transactional.TxType.SUPPORTS)
public Stream<Theme> findThemeByNames(Set<String> themeNames) {
try {
var cb = this.getEntityManager().getCriteriaBuilder();
var cq = cb.createQuery(Theme.class);
var root = cq.from(Theme.class);

if (themeNames != null && !themeNames.isEmpty()) {
cq.where(root.get(Theme_.name).in(themeNames));
}

return this.getEntityManager().createQuery(cq).getResultStream();

} catch (Exception ex) {
throw new DAOException(ErrorKeys.ERROR_FIND_THEME_BY_NAMES, ex);
}
}

public Theme findThemeByName(String themeName) {
try {
var cb = this.getEntityManager().getCriteriaBuilder();
var cq = cb.createQuery(Theme.class);
var root = cq.from(Theme.class);
cq.where(cb.equal(root.get(Theme_.name), themeName));

return this.em.createQuery(cq).getSingleResult();
return this.getEntityManager().createQuery(cq).getSingleResult();

} catch (NoResultException nre) {
return null;
Expand All @@ -31,6 +52,8 @@ public Theme findThemeByName(String themeName) {
}

public enum ErrorKeys {

ERROR_FIND_THEME_BY_NAMES,
ERROR_FIND_THEME_BY_NAME,
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package io.github.onecx.theme.domain.di.mappers;

import java.util.ArrayList;
import java.util.List;

import jakarta.inject.Inject;

import org.mapstruct.IterableMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
Expand All @@ -14,6 +14,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;

import gen.io.github.onecx.theme.di.v1.model.DataImportThemeDTOV1;
import gen.io.github.onecx.theme.di.v1.model.DataImportThemesDTOV1;
import io.github.onecx.theme.domain.models.Theme;

@Mapper(uses = OffsetDateTimeMapper.class)
Expand All @@ -22,11 +23,23 @@ public abstract class DataImportMapperV1 {
@Inject
ObjectMapper mapper;

@IterableMapping(qualifiedByName = "import")
public abstract List<Theme> importThemes(List<DataImportThemeDTOV1> dto);
public List<Theme> importThemes(DataImportThemesDTOV1 request) {
List<Theme> result = new ArrayList<>();
if (request == null) {
return result;
}
request.forEach((name, dto) -> {
var theme = importTheme(dto);
theme.setName(name);
result.add(theme);
});

return result;
}

@Named("import")
@Mapping(target = "id", ignore = true)
@Mapping(target = "name", ignore = true)
@Mapping(target = "properties", qualifiedByName = "properties")
@Mapping(target = "creationDate", ignore = true)
@Mapping(target = "creationUser", ignore = true)
Expand All @@ -35,7 +48,7 @@ public abstract class DataImportMapperV1 {
@Mapping(target = "controlTraceabilityManual", ignore = true)
@Mapping(target = "modificationCount", ignore = true)
@Mapping(target = "persisted", ignore = true)
public abstract Theme theme(DataImportThemeDTOV1 dto);
public abstract Theme importTheme(DataImportThemeDTOV1 dto);

@Named("properties")
String properties(Object properties) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package io.github.onecx.theme.rs.exim.v1.controllers;

import java.util.stream.Collectors;

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

import org.jboss.resteasy.reactive.RestResponse;
import org.jboss.resteasy.reactive.server.ServerExceptionMapper;

import gen.io.github.onecx.theme.rs.exim.v1.ThemesExportImportApi;
import gen.io.github.onecx.theme.rs.exim.v1.model.*;
import io.github.onecx.theme.domain.daos.ThemeDAO;
import io.github.onecx.theme.domain.models.Theme;
import io.github.onecx.theme.rs.exim.v1.mappers.ExportImportExceptionMapperV1;
import io.github.onecx.theme.rs.exim.v1.mappers.ExportImportMapperV1;

@Path("/exim/v1/themes")
@ApplicationScoped
@Transactional(Transactional.TxType.NOT_SUPPORTED)
public class ExportImportRestControllerV1 implements ThemesExportImportApi {

@Inject
ThemeDAO dao;
@Inject
ExportImportExceptionMapperV1 exceptionMapper;

@Inject
ExportImportMapperV1 mapper;

@Override
public Response exportThemes(EximExportRequestDTOV1 request) {
var themes = dao.findThemeByNames(request.getNames());

var data = themes.collect(Collectors.toMap(Theme::getName, theme -> theme));

if (data.isEmpty()) {
return Response.status(Response.Status.NOT_FOUND).build();
}
return Response.ok(mapper.create(data)).build();
}

@Override
@Transactional(Transactional.TxType.REQUIRES_NEW)
public Response importThemes(EximImportRequestDTOV1 request) {
var keys = request.getThemes().keySet();
var themes = dao.findThemeByNames(keys);
var map = themes.collect(Collectors.toMap(Theme::getName, theme -> theme));

EximImportResultThemesDTOV1 items = new EximImportResultThemesDTOV1();

request.getThemes().forEach((name, dto) -> {

var theme = map.get(name);
if (theme == null) {

theme = mapper.create(dto);
theme.setName(name);
dao.create(theme);
items.put(name, mapper.create(EximThemeResultStatusDTOV1.CREATED));

} else {

mapper.update(dto, theme);
dao.update(theme);
items.put(name, mapper.create(EximThemeResultStatusDTOV1.UPDATE));
}
});

return Response.ok(mapper.create(request, items)).build();
}

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

@ServerExceptionMapper
public RestResponse<EximRestExceptionDTOV1> constraint(ConstraintViolationException ex) {
return exceptionMapper.constraint(ex);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package io.github.onecx.theme.rs.exim.v1.mappers;

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

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.DAOException;
import org.tkit.quarkus.rs.mappers.OffsetDateTimeMapper;

import gen.io.github.onecx.theme.rs.exim.v1.model.*;
import lombok.extern.slf4j.Slf4j;

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

public RestResponse<EximRestExceptionDTOV1> constraint(ConstraintViolationException ex) {
log.error("Processing theme export import rest controller error: {}", ex.getMessage());

var dto = exception("CONSTRAINT_VIOLATIONS", ex.getMessage());
dto.setValidations(createErrorValidationResponse(ex.getConstraintViolations()));
return RestResponse.status(Response.Status.BAD_REQUEST, dto);
}

public RestResponse<EximRestExceptionDTOV1> exception(Exception ex) {
log.error("Processing theme export import rest controller error: {}", ex.getMessage());

if (ex instanceof DAOException de) {
return RestResponse.status(Response.Status.BAD_REQUEST,
exception(de.getMessageKey().name(), ex.getMessage(), de.parameters));
}
return RestResponse.status(Response.Status.INTERNAL_SERVER_ERROR,
exception("UNDEFINED_ERROR_CODE", ex.getMessage()));

}

@Mapping(target = "removeParametersItem", ignore = true)
@Mapping(target = "namedParameters", ignore = true)
@Mapping(target = "removeNamedParametersItem", ignore = true)
@Mapping(target = "parameters", ignore = true)
@Mapping(target = "validations", ignore = true)
@Mapping(target = "removeValidationsItem", ignore = true)
public abstract EximRestExceptionDTOV1 exception(String errorCode, String message);

@Mapping(target = "removeParametersItem", ignore = true)
@Mapping(target = "namedParameters", ignore = true)
@Mapping(target = "removeNamedParametersItem", ignore = true)
@Mapping(target = "validations", ignore = true)
@Mapping(target = "removeValidationsItem", ignore = true)
public abstract EximRestExceptionDTOV1 exception(String errorCode, String message, List<Object> parameters);

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

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

public String mapPath(Path path) {
return path.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package io.github.onecx.theme.rs.exim.v1.mappers;

import java.time.OffsetDateTime;
import java.util.Map;

import jakarta.inject.Inject;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Named;
import org.tkit.quarkus.rs.mappers.OffsetDateTimeMapper;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import gen.io.github.onecx.theme.rs.exim.v1.model.*;
import io.github.onecx.theme.domain.models.Theme;

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

@Inject
ObjectMapper objectMapper;

@Mapping(target = "id", source = "request.id")
@Mapping(target = "themes", source = "themes")
public abstract EximImportResultDTOV1 create(EximImportRequestDTOV1 request, EximImportResultThemesDTOV1 themes);

public abstract EximThemeResultDTOV1 create(EximThemeResultStatusDTOV1 status);

public EximImportRequestDTOV1 create(Map<String, Theme> data) {
if (data == null) {
return null;
}
EximImportRequestDTOV1 result = new EximImportRequestDTOV1();
result.setCreated(OffsetDateTime.now());
result.setThemes(map(data));
return result;
}

public abstract EximImportThemesDTOV1 map(Map<String, Theme> data);

public abstract EximThemeDTOV1 map(Theme theme);

@Mapping(target = "id", ignore = true)
@Mapping(target = "name", ignore = true)
@Mapping(target = "creationDate", ignore = true)
@Mapping(target = "creationUser", ignore = true)
@Mapping(target = "modificationDate", ignore = true)
@Mapping(target = "modificationUser", ignore = true)
@Mapping(target = "controlTraceabilityManual", ignore = true)
@Mapping(target = "modificationCount", ignore = true)
@Mapping(target = "persisted", ignore = true)
@Mapping(target = "properties", qualifiedByName = "properties")
public abstract void update(EximThemeDTOV1 dto, @MappingTarget Theme entity);

@Mapping(target = "id", ignore = true)
@Mapping(target = "name", ignore = true)
@Mapping(target = "creationDate", ignore = true)
@Mapping(target = "creationUser", ignore = true)
@Mapping(target = "modificationDate", ignore = true)
@Mapping(target = "modificationUser", ignore = true)
@Mapping(target = "controlTraceabilityManual", ignore = true)
@Mapping(target = "modificationCount", ignore = true)
@Mapping(target = "persisted", ignore = true)
@Mapping(target = "properties", qualifiedByName = "properties")
public abstract Theme create(EximThemeDTOV1 dto);

@Named("properties")
public String mapToString(Object properties) {

if (properties == null) {
return null;
}

try {
return objectMapper.writeValueAsString(properties);
} catch (JsonProcessingException e) {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@
public abstract class ExceptionMapper {

public RestResponse<RestExceptionDTO> constraint(ConstraintViolationException ex) {
log.error("Processing theme internal rest controller error: {}", ex.getMessage());

var dto = exception("CONSTRAINT_VIOLATIONS", ex.getMessage());
dto.setValidations(createErrorValidationResponse(ex.getConstraintViolations()));
return RestResponse.status(Response.Status.BAD_REQUEST, dto);
}

public RestResponse<RestExceptionDTO> exception(Exception ex) {
log.error("Processing portal internal rest controller error: {}", ex.getMessage());
log.error("Processing theme internal rest controller error: {}", ex.getMessage());

ex.printStackTrace();
if (ex instanceof DAOException de) {
return RestResponse.status(Response.Status.BAD_REQUEST,
exception(de.getMessageKey().name(), ex.getMessage(), de.parameters));
Expand Down
Loading

0 comments on commit f916abb

Please sign in to comment.