Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
ca.gc.aafc.dina.repository.DinaRepository Maven / Gradle / Ivy
Go to download
Base DINA API package for Java built on SpringBoot and Crnk
package ca.gc.aafc.dina.repository;
import ca.gc.aafc.dina.entity.DinaEntity;
import ca.gc.aafc.dina.filter.DinaFilterResolver;
import ca.gc.aafc.dina.mapper.DinaMapper;
import ca.gc.aafc.dina.mapper.DinaMappingLayer;
import ca.gc.aafc.dina.mapper.DinaMappingRegistry;
import ca.gc.aafc.dina.repository.auditlog.AuditSnapshotRepository;
import ca.gc.aafc.dina.repository.external.ExternalResourceProvider;
import ca.gc.aafc.dina.repository.meta.DinaMetaInfo;
import ca.gc.aafc.dina.security.DinaAuthorizationService;
import ca.gc.aafc.dina.service.AuditService;
import ca.gc.aafc.dina.service.DinaService;
import io.crnk.core.engine.internal.utils.PropertyUtils;
import io.crnk.core.exception.ResourceNotFoundException;
import io.crnk.core.queryspec.QuerySpec;
import io.crnk.core.repository.MetaRepository;
import io.crnk.core.repository.ResourceRepository;
import io.crnk.core.resource.list.DefaultResourceList;
import io.crnk.core.resource.list.ResourceList;
import io.crnk.core.resource.meta.DefaultPagedMetaInformation;
import io.crnk.core.resource.meta.MetaInformation;
import io.crnk.core.resource.meta.PagedMetaInformation;
import lombok.Getter;
import lombok.NonNull;
import lombok.SneakyThrows;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.boot.info.BuildProperties;
import javax.transaction.Transactional;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
/**
* JSONAPI repository that interfaces using DTOs, and uses JPA entities internally. Sparse fields
* sets are handled by the underlying Crnk ResourceRepository.
*
* @param - Dto type
* @param - Entity type
*/
@Transactional
public class DinaRepository
implements ResourceRepository, MetaRepository {
/* Forces CRNK to not display any top-level links. */
private static final NoLinkInformation NO_LINK_INFORMATION = new NoLinkInformation();
private static final long DEFAULT_LIMIT = 100;
@Getter
private final Class resourceClass;
private final Class entityClass;
private final DinaService dinaService;
private final Optional authorizationService;
private final Optional auditService;
private final DinaMappingLayer mappingLayer;
private final DinaFilterResolver filterResolver;
private final List> externalMetaMap;
private final BuildProperties buildProperties;
private final DinaMappingRegistry registry;
private final boolean hasFieldAdapters;
public DinaRepository(
@NonNull DinaService dinaService,
@NonNull Optional authorizationService,
@NonNull Optional auditService,
@NonNull DinaMapper dinaMapper,
@NonNull Class resourceClass,
@NonNull Class entityClass,
DinaFilterResolver filterResolver,
ExternalResourceProvider externalResourceProvider,
@NonNull BuildProperties buildProperties
) {
this.dinaService = dinaService;
this.authorizationService = authorizationService;
this.auditService = auditService;
this.resourceClass = resourceClass;
this.entityClass = entityClass;
this.filterResolver = Objects.requireNonNullElseGet(
filterResolver, () -> new DinaFilterResolver(null));
this.buildProperties = buildProperties;
if (externalResourceProvider != null) {
this.externalMetaMap =
DinaMetaInfo.parseExternalTypes(resourceClass, externalResourceProvider);
} else {
this.externalMetaMap = null;
}
this.registry = new DinaMappingRegistry(resourceClass);
this.mappingLayer = new DinaMappingLayer<>(resourceClass, dinaMapper, dinaService, this.registry);
this.hasFieldAdapters = CollectionUtils.isNotEmpty(registry.getFieldAdaptersPerClass().keySet());
}
/**
* Returns a resource with a given id. Relations that are not included in the query spec are
* mapped in a shallow form. Relations included in the query spec are eager loaded.
*
* @param querySpec - query spec of the request
* @return - list of resources
*/
@Override
public D findOne(Serializable id, QuerySpec querySpec) {
querySpec.setLimit(1L);
ResourceList resourceList = findAll(Collections.singletonList(id), querySpec);
if (resourceList.size() == 0) {
auditService.ifPresent(service -> { // Past Deleted records with audit logs throw Gone.
final String resourceType = querySpec.getResourceType();
final AuditService.AuditInstance auditInstance = AuditService.AuditInstance.builder()
.id(id.toString()).type(resourceType).build();
if (service.hasTerminalSnapshot(auditInstance)) {
throw new GoneException(
"GONE",
"The Resource has been deleted but audit records remain, see the links.about section",
AuditSnapshotRepository.generateUrlLink(resourceType, id.toString()));
}
});
throw new ResourceNotFoundException(
resourceClass.getSimpleName() + " with ID " + id + " Not Found.");
}
return resourceList.get(0);
}
/**
* Returns a list of resources from a given query spec. Relations that are not included in the
* query spec are mapped in a shallow form. Relations included in the query spec are eager
* loaded.
*
* @param querySpec - query spec of the request
* @return - list of resources
*/
@Override
public ResourceList findAll(QuerySpec querySpec) {
return findAll(null, querySpec);
}
/**
* Returns a list of resources from a given collection of ids and a query spec. Relations that are
* not included in the query spec are mapped in a shallow form. Relations included in the query
* spec are eager loaded.
*
* @param ids - ids to query
* @param querySpec - query spec of the request
* @return - list of resources
*/
@Override
public ResourceList findAll(Collection ids, QuerySpec querySpec) {
final QuerySpec spec = resolveFilterAdapters(querySpec);
String idName = findIdFieldName(resourceClass);
List dList = mappingLayer.mapEntitiesToDto(spec, fetchEntities(ids, spec, idName));
Long resourceCount = dinaService.getResourceCount( entityClass,
(criteriaBuilder, root, em) -> filterResolver.buildPredicates(spec, criteriaBuilder, root, ids, idName, em));
DefaultPagedMetaInformation metaInformation = new DefaultPagedMetaInformation();
metaInformation.setTotalResourceCount(resourceCount);
return new DefaultResourceList<>(dList, metaInformation, NO_LINK_INFORMATION);
}
/**
* Convenience method to resolve the filters of a given query spec for {@link
* ca.gc.aafc.dina.mapper.DinaFieldAdapter}'s. A QuerySpec will only be processed if a resources entity
* graph contains any field adapters.
*
* @param querySpec - QuerySpec with filters to resolve
* @return A new QuerySpec with the resolved filters, or the original query spec.
*/
private QuerySpec resolveFilterAdapters(QuerySpec querySpec) {
if (hasFieldAdapters) {
QuerySpec spec = querySpec.clone();
spec.setFilters(
DinaFilterResolver.resolveFilterAdapters(resourceClass, querySpec.getFilters(), registry));
return spec;
}
return querySpec;
}
private List fetchEntities(Collection ids, QuerySpec querySpec, String idName) {
return dinaService.findAll(
entityClass,
(criteriaBuilder, root, em) -> {
DinaFilterResolver.leftJoinRelations(root, querySpec, registry);
return filterResolver.buildPredicates(querySpec, criteriaBuilder, root, ids, idName, em);
},
(cb, root) -> DinaFilterResolver.getOrders(querySpec, cb, root),
Math.toIntExact(querySpec.getOffset()),
Optional.ofNullable(querySpec.getLimit()).orElse(DEFAULT_LIMIT).intValue());
}
@Override
public S save(S resource) {
Object id = PropertyUtils.getProperty(resource, findIdFieldName(resourceClass));
E entity = dinaService.findOne(id, entityClass);
authorizationService.ifPresent(auth -> auth.authorizeUpdate(entity));
if (entity == null) {
throw new ResourceNotFoundException(
resourceClass.getSimpleName() + " with ID " + id + " Not Found.");
}
mappingLayer.mapToEntity(resource, entity);
dinaService.update(entity);
auditService.ifPresent(service -> service.audit(resource));
return resource;
}
@Override
@SneakyThrows
@SuppressWarnings("unchecked")
public S create(S resource) {
E entity = entityClass.getConstructor().newInstance();
mappingLayer.mapToEntity(resource, entity);
authorizationService.ifPresent(auth -> auth.authorizeCreate(entity));
dinaService.create(entity);
D dto = findOne(
(Serializable) PropertyUtils.getProperty(entity, findIdFieldName(resourceClass)),
new QuerySpec(resourceClass));
auditService.ifPresent(service -> service.audit(dto));
return (S) dto;
}
@Override
public void delete(Serializable id) {
E entity = dinaService.findOne(id, entityClass);
if (entity == null) {
throw new ResourceNotFoundException(
resourceClass.getSimpleName() + " with ID " + id + " Not Found.");
}
authorizationService.ifPresent(auth -> auth.authorizeDelete(entity));
dinaService.delete(entity);
auditService.ifPresent(service -> service.auditDeleteEvent(mappingLayer.toDtoSimpleMapping(
entity)));
}
@Override
public DinaMetaInfo getMetaInformation(
Collection collection, QuerySpec querySpec, MetaInformation metaInformation
) {
DinaMetaInfo metaInfo = new DinaMetaInfo();
// Set External types
metaInfo.setExternal(externalMetaMap);
// Set resource counts
if (metaInformation instanceof PagedMetaInformation) {
PagedMetaInformation pagedMetaInformation = (PagedMetaInformation) metaInformation;
if (pagedMetaInformation.getTotalResourceCount() != null) {
metaInfo.setTotalResourceCount(pagedMetaInformation.getTotalResourceCount());
}
} else {
metaInfo.setTotalResourceCount((long) collection.size());
}
metaInfo.setModuleVersion(buildProperties.getVersion());
return metaInfo;
}
@SneakyThrows
public void validate(D resource) {
E entity = entityClass.getConstructor().newInstance();
mappingLayer.applySimpleMappingToEntity(resource, entity);
// validation group should probably be set here
dinaService.validateConstraints(entity, null);
dinaService.validateBusinessRules(entity);
}
/**
* Returns the id field name for a given class.
*
* @param clazz - class to find the id field name for
* @return - id field name for a given class.
*/
private String findIdFieldName(Class> clazz) {
return this.registry.findJsonIdFieldName(clazz);
}
}