Skip to content

Commit

Permalink
Version 0.1.0
Browse files Browse the repository at this point in the history
Unify version management
Refactor RelationsManager and Mappers generated code
Extend ApigenRepository default methods
Document extension strategies
  • Loading branch information
adrian-palanques-cloudappi committed Nov 11, 2021
1 parent 011a6e8 commit 080cbe2
Show file tree
Hide file tree
Showing 22 changed files with 437 additions and 94 deletions.
142 changes: 141 additions & 1 deletion archetype-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,147 @@ Ejemplo:

spring.jpa.properties.hibernate.dialect = my.project.dialects.dialects.MyH2Dialect


# Extender los principales componentes sin modificar el código generado

Si queremos hacer uso al máximo de la generación a partir del OpenAPI, lo recomendable es no modificar manualmente el código generado, o en su defecto lo mínimo posible.

## Extender un servicio

````java
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

@Slf4j
@Primary
@Service
public class FooServiceExt extends FooService {

// Constructor ...

@Override
protected void preSave(Foo foo) {
log.info("ALL OK");
}
}
````

## Extender un repositorio

````java
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Repository;

@Primary
@Repository
public interface FooRepositoryExt extends FooRepository {
long countDistinctByBar(Bar bar);
}
````

## Extender un mapper

````java
import lombok.extern.slf4j.Slf4j;
import org.apiaddicts.apitools.apigen.archetypecore.exceptions.RelationalErrors;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Slf4j
@Primary
@Component
public class FooRelationManagerExt extends FooRelationManager {

@Override
protected void createOrRetrieveRelationsBar(Foo foo, RelationalErrors errors) {
// Perform some relational validation or other logic
foo.setBar(createOrRetrieve(foo.getBar(), barService, errors));
}
}
````

## Extender un relations manager

````java
import lombok.extern.slf4j.Slf4j;
import org.mapstruct.*;
import org.springframework.context.annotation.Primary;

@Primary
@Slf4j
@Mapper(componentModel = "spring", uses = {BarMapper.class})
@DecoratedWith(FooMapperExtDecorator.class) // Only required if you need to change the mapper logic, not required if you can manage the changes with hooks
public abstract class FooMapperExt extends StayMapper {

@Named("updateBasicData")
@BeforeMapping
public void beforeBasicDataUpdate(Foo source, @MappingTarget Foo target) {
log.info("Before mapping");
}

@Named("updateBasicData")
@AfterMapping
public void afterBasicDataUpdate(Foo source, @MappingTarget Foo target) {
log.info("After mapping");
}
}
````

````java
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

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

@Slf4j
@NoArgsConstructor
@AllArgsConstructor
public class FooMapperExtDecorator extends FooMapperExt {

@Autowired
@Qualifier("delegate")
private StayMapperExt mapper;

@Override
public FooOutResource toResource(Foo entity) {
return mapper.toResource(entity);
}

@Override
public List<FooOutResource> toResource(List<Foo> entities) {
return mapper.toResource(entities);
}

@Override
public Set<FooOutResource> toResource(Set<Foo> entities) {
return mapper.toResource(entities);
}

@Override
public Foo toEntity(CreateFooResource resource) {
return mapper.toEntity(resource);
}

@Override
public Foo toEntity(UpdateFooByIdResource resource) {
return mapper.toEntity(resource);
}

@Override
public void updateBasicData(Foo source, Foo target) {
// Simple example to keep a property without modifying the original mapper
String keep = target.getCause();
mapper.updateBasicData(source, target);
target.setCause(keep);
}
}
````

# Tabla descriptiva del comportamiento por defecto en la creación

| Tipo de campo | Dato en el json | Resultado | Notas |
Expand Down Expand Up @@ -337,4 +477,4 @@ Relación de tipos con bases de datos más comunes:

# Health check

Por defecto viene configurado Actuator para que solo tenga activa la url de heakth check en `http://HOST:PORT/actuator/health`, para mas información consultar la documentación de Spring Boot Actuator y sus properties.
Por defecto viene configurado Actuator para que solo tenga activa la url de heakth check en `http://HOST:PORT/actuator/health`, para mas información consultar la documentación de Spring Boot Actuator y sus properties.
4 changes: 2 additions & 2 deletions archetype-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>org.apiaddicts.apitools.apigen</groupId>
<artifactId>apigen</artifactId>
<version>0.0.3</version>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down Expand Up @@ -102,4 +102,4 @@

</dependencies>

</project>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

public class AbstractRelationsManager<E> {

Expand All @@ -22,21 +25,30 @@ public void updateRelations(E persistedEntity, E entity, Set<String> fields) {
}

protected <T extends ApigenAbstractPersistable<K>, K extends Serializable> T retrieve(T entity, AbstractReadService<T, K, ?> service, RelationalErrors errors) {
if (entity == null) return null;
K id = entity.getId();
T retrieved = (id == null) ? null : service.getOne(id).orElse(null);
if (retrieved == null) errors.register(entity.getClass(), id);
return retrieved;
}

protected <T extends ApigenAbstractPersistable<K>, K extends Serializable> Set<T> retrieve(Set<T> entities, AbstractReadService<T, K, ?> service, RelationalErrors errors) {
if (entities == null) return new HashSet<>();
return entities.stream().map(e -> retrieve(e, service, errors)).collect(Collectors.toSet());
}

protected <T extends ApigenAbstractPersistable<?>> T create(T entity, AbstractCrudService<T, ?, ?> service) {
if (entity == null) return null;
return service.create(entity);
}

protected <T extends ApigenAbstractPersistable<?>> void delete(T entity, AbstractCrudService<T, ?, ?> service) {
if (entity == null) return;
service.delete(entity);
}

protected <T extends ApigenAbstractPersistable<K>, K extends Serializable> T createOrRetrieve(T entity, AbstractCrudService<T, K, ?> service, RelationalErrors errors) {
if (entity == null) return null;
if (entity.isReference()) {
return retrieve(entity, service, errors);
} else {
Expand All @@ -48,4 +60,9 @@ protected <T extends ApigenAbstractPersistable<K>, K extends Serializable> T cre
}
}
}

protected <T extends ApigenAbstractPersistable<K>, K extends Serializable> Set<T> createOrRetrieve(Set<T> entities, AbstractCrudService<T, K, ?> service, RelationalErrors errors) {
if (entities == null) return new HashSet<>();
return entities.stream().map(e -> createOrRetrieve(e, service, errors)).collect(Collectors.toSet());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ public interface ApigenRepository<E, K extends Serializable> extends JpaReposito
ApigenSearchResult<E> search(ApigenSearch search);

Optional<E> searchById(K id, ApigenSearch search);
}

long count(ApigenSearch search);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ public ApigenSearchResult<E> search(ApigenSearch search) {

@Override
public Optional<E> searchById(K id, ApigenSearch search) {
return searchExecutor.searchById(id, search.getSelect(), search.getExclude(), search.getOrderBy(), search.getExpand(), clazz);
return searchExecutor.searchById(id, search.getSelect(), search.getExclude(), search.getOrderBy(), search.getExpand(), search.getFilter(), clazz);
}

@Override
public long count(ApigenSearch search) {
return searchExecutor.count(search.getExpand(), search.getFilter(), clazz);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public ApigenSearchExecutor(EntityManager em, EntityInfo entityData) {
this.entityData = entityData;
}

public <E, K extends Serializable> Optional<E> searchById(K id, List<String> select, List<String> exclude, List<String> orderBy, List<String> expand, Class<E> clazz) {
public <E, K extends Serializable> Optional<E> searchById(K id, List<String> select, List<String> exclude, List<String> orderBy, List<String> expand, Filter filter, Class<E> clazz) {

CriteriaBuilder builder = em.getCriteriaBuilder();

Expand All @@ -50,8 +50,12 @@ public <E, K extends Serializable> Optional<E> searchById(K id, List<String> sel
String idAttribute = entityData.getIdAttribute(clazz);
Expression expression = getPath(idAttribute, root, joins);
Predicate predicate = builder.equal(expression, id);
Predicate conditionalPredicate = filter(filter, builder, root, joins);

if (predicate != null) query.where(predicate);
if (conditionalPredicate != null) {
predicate = builder.and(predicate, conditionalPredicate);
}
query.where(predicate);
TypedQuery<Tuple> createQuery = em.createQuery(query);
List<Tuple> result = createQuery.getResultList();
List<E> found = new TupleMapper(enhancedFields, entityData, clazz).map(result);
Expand Down Expand Up @@ -91,6 +95,11 @@ public <E> ApigenSearchResult<E> search(List<String> select, List<String> exclud
return new ApigenSearchResult<>(new TupleMapper(enhancedFields, entityData, clazz).map(result), count);
}

public long count(List<String> expand, Filter filter, Class clazz) {
CriteriaBuilder builder = em.getCriteriaBuilder();
return count(clazz, expand, filter, builder);
}

private void addPaginationPredicate(CriteriaQuery<Tuple> query, Root<?> root, List<String> orderBy, List<String> expand, Filter filter, Pagination pagination, Class<?> clazz, CriteriaBuilder builder) { // NOSONAR
List<Object> ids = getMatchingIds(orderBy, expand, filter, pagination, clazz, builder);
String idAttribute = entityData.getIdAttribute(clazz);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
import org.apiaddicts.apitools.apigen.archetypecore.core.persistence.filter.FilterOperation;
import org.apiaddicts.apitools.apigen.archetypecore.core.persistence.filter.Value;
import org.apiaddicts.apitools.apigen.archetypecore.core.persistence.pagination.Pagination;
import org.apiaddicts.apitools.apigen.archetypecore.core.persistence.stubs.FakeEntityDates;
import org.apiaddicts.apitools.apigen.archetypecore.core.persistence.stubs.FakeEntityDatesRepository;
import org.apiaddicts.apitools.apigen.archetypecore.core.persistence.stubs.FakeEntityNode;
import org.apiaddicts.apitools.apigen.archetypecore.core.persistence.stubs.FakeEntityNodeRepository;
import org.apiaddicts.apitools.apigen.archetypecore.core.persistence.stubs.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
Expand All @@ -35,6 +34,14 @@ class ApigenRepositoryTest {

@Autowired FakeEntityDatesRepository datesRepository;
@Autowired FakeEntityNodeRepository nodeRepository;
@Autowired FakeEntityBigEntityRepository bigEntityRepository;

@Test
void givenPersistedRecords_whenCount_thenCount() {
ApigenSearch search = new ApigenSearch(EMPTY, EMPTY, EMPTY, null, EMPTY, null, false);
long result = datesRepository.count(search);
assertEquals(TOTAL_DATES_ENTITIES, result);
}

@Test
void givenPersistedRecords_whenSearchedPaginated_thenSearchAll() {
Expand Down Expand Up @@ -134,4 +141,29 @@ void givenPersistedRecords_whenExpandAndSelect_thenSuccess() {
assertNotNull(north.getChildren());
assertEquals(1, north.getChildren().size());
}
}

@Test
void givenBigAttributes_whenPersist_thenSuccess() {
FakeEntityBigEntity e = new FakeEntityBigEntity();
e.setBDec(BigDecimal.valueOf(.333333));
e.setBInt(BigInteger.valueOf(1000));
bigEntityRepository.save(e);

assertNotNull(e.getId());

bigEntityRepository.delete(e);
}

@Test
void givenBigAttributes_whenSearch_thenSuccess() {
Filter filter = new Filter();
filter.setOperation(FilterOperation.LT);
Value value = new Value();
value.setProperty("bDec");
value.setValue("0.3333");
filter.setValues(Collections.singletonList(value));
ApigenSearch search = new ApigenSearch(EMPTY, EMPTY, EMPTY, filter, EMPTY, null, false);
ApigenSearchResult<FakeEntityBigEntity> result = bigEntityRepository.search(search);
assertEquals(2, result.getSearchResult().size());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.apiaddicts.apitools.apigen.archetypecore.core.persistence.stubs;

import lombok.Getter;
import lombok.Setter;
import org.apiaddicts.apitools.apigen.archetypecore.core.persistence.ApigenAbstractPersistable;

import javax.persistence.*;
import java.math.BigDecimal;
import java.math.BigInteger;

@Getter
@Setter
@Entity
@Table(name = "big_entity")
public class FakeEntityBigEntity extends ApigenAbstractPersistable<Long> {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private BigInteger bInt;
private BigDecimal bDec;


@Override
public Long getId() {
return id;
}

@Override
public void setId(Long id) {
this.id = id;
}

@Override
public boolean isReference() {
return getId() != null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.apiaddicts.apitools.apigen.archetypecore.core.persistence.stubs;

import org.apiaddicts.apitools.apigen.archetypecore.core.persistence.ApigenRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface FakeEntityBigEntityRepository extends ApigenRepository<FakeEntityBigEntity, Long> {
}
Loading

0 comments on commit 080cbe2

Please sign in to comment.