Skip to content

Commit

Permalink
Merge branch 'dev' into 35276_add_metagenomic_workflow_resource
Browse files Browse the repository at this point in the history
  • Loading branch information
cgendreau committed Nov 27, 2024
2 parents afc00d1 + 21fa0bf commit 5386a12
Show file tree
Hide file tree
Showing 15 changed files with 583 additions and 2 deletions.
Binary file modified docs/sequencingModule.drawio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package ca.gc.aafc.seqdb.api.dto;

import ca.gc.aafc.dina.dto.RelatedEntity;
import ca.gc.aafc.dina.i18n.MultilingualDescription;
import ca.gc.aafc.dina.vocabulary.TypedVocabularyElement;
import ca.gc.aafc.seqdb.api.entities.SequenceManagedAttribute;

import io.crnk.core.resource.annotations.JsonApiId;
import io.crnk.core.resource.annotations.JsonApiResource;
import lombok.Data;

import java.time.OffsetDateTime;
import java.util.UUID;

import org.javers.core.metamodel.annotation.Id;
import org.javers.core.metamodel.annotation.PropertyName;
import org.javers.core.metamodel.annotation.TypeName;

@RelatedEntity(SequenceManagedAttribute.class)
@Data
@JsonApiResource(type = SequenceManagedAttributeDto.TYPENAME)
@TypeName(SequenceManagedAttributeDto.TYPENAME)
public class SequenceManagedAttributeDto {
public static final String TYPENAME = "managed-attribute";

@JsonApiId
@Id
@PropertyName("id")
private UUID uuid;
private String name;
private String key;
private TypedVocabularyElement.VocabularyElementType vocabularyElementType;
private String unit;
private SequenceManagedAttribute.ManagedAttributeComponent managedAttributeComponent;
private String[] acceptedValues;
private OffsetDateTime createdOn;
private String createdBy;
private String group;
private MultilingualDescription multilingualDescription;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package ca.gc.aafc.seqdb.api.entities;

import java.time.OffsetDateTime;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.Getter;

import org.hibernate.annotations.Generated;
import org.hibernate.annotations.GenerationTime;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.NaturalIdCache;
import org.hibernate.annotations.Type;

import ca.gc.aafc.dina.entity.DinaEntityIdentifiableByName;
import ca.gc.aafc.dina.entity.ManagedAttribute;
import ca.gc.aafc.dina.i18n.MultilingualDescription;
import ca.gc.aafc.dina.i18n.MultilingualTitle;

@Entity(name = "managed_attribute")
@Getter
@Setter
@Builder
@AllArgsConstructor
@RequiredArgsConstructor
@NaturalIdCache
public class SequenceManagedAttribute implements ManagedAttribute, DinaEntityIdentifiableByName {

public enum ManagedAttributeComponent {
GENERIC_MOLECULAR_ANALYSIS;

public static ManagedAttributeComponent fromString(String s) {
for (ManagedAttributeComponent source : ManagedAttributeComponent.values()) {
if (source.name().equalsIgnoreCase(s)) {
return source;
}
}
return null;
}
}

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

@NaturalId
@NotNull
@Column(name = "uuid", unique = true)
private UUID uuid;

@Column(name = "created_on", insertable = false, updatable = false)
@Generated(value = GenerationTime.INSERT)
private OffsetDateTime createdOn;

@NotBlank
@Column(name = "created_by", updatable = false)
private String createdBy;

@NotBlank
private String name;

@Type(type = "jsonb")
@Column(name = "multilingual_description")
@Valid
private MultilingualDescription multilingualDescription;

@NotBlank
@Column(name = "_group")
@Size(max = 50)
private String group;

@NotBlank
@Size(max = 50)
@Column(updatable = false)
private String key;

@NotNull
@Type(type = "pgsql_enum")
@Enumerated(EnumType.STRING)
@Column(name = "type")
private VocabularyElementType vocabularyElementType;

@NotNull
@Enumerated(EnumType.STRING)
@Column(name = "component")
private ManagedAttributeComponent managedAttributeComponent;

@Type(type = "string-array")
@Column(name = "accepted_values", columnDefinition = "text[]")
private String[] acceptedValues;

@Size(max = 50)
private String unit;

@Override
public String getTerm() {
return null;
}

@Override
public MultilingualTitle getMultilingualTitle() {
return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package ca.gc.aafc.seqdb.api.repository;

import org.springframework.boot.info.BuildProperties;
import org.springframework.stereotype.Repository;

import com.fasterxml.jackson.databind.ObjectMapper;

import ca.gc.aafc.dina.mapper.DinaMapper;
import ca.gc.aafc.dina.repository.DinaRepository;
import ca.gc.aafc.dina.repository.external.ExternalResourceProvider;
import ca.gc.aafc.dina.security.DinaAuthenticatedUser;
import ca.gc.aafc.dina.security.TextHtmlSanitizer;
import ca.gc.aafc.dina.service.AuditService;
import ca.gc.aafc.seqdb.api.dto.SequenceManagedAttributeDto;
import ca.gc.aafc.seqdb.api.entities.SequenceManagedAttribute;
import ca.gc.aafc.seqdb.api.security.SuperUserInGroupCUDAuthorizationService;
import ca.gc.aafc.seqdb.api.service.SequenceManagedAttributeService;

import io.crnk.core.exception.ResourceNotFoundException;
import io.crnk.core.queryspec.QuerySpec;
import java.io.Serializable;
import java.util.Optional;
import java.util.regex.Pattern;
import lombok.NonNull;

@Repository
public class SequenceManagedAttributeRepository extends DinaRepository<SequenceManagedAttributeDto, SequenceManagedAttribute> {

private final SequenceManagedAttributeService dinaService;
private Optional<DinaAuthenticatedUser> dinaAuthenticatedUser;

public static final Pattern KEY_LOOKUP_PATTERN = Pattern.compile("(.*)\\.(.*)");

public SequenceManagedAttributeRepository(
@NonNull SequenceManagedAttributeService service,
@NonNull SuperUserInGroupCUDAuthorizationService authService,
ExternalResourceProvider externalResourceProvider,
@NonNull AuditService auditService,
@NonNull BuildProperties buildProperties,
Optional<DinaAuthenticatedUser> dinaAuthenticatedUser,
ObjectMapper objectMapper
) {
super(
service, authService,
Optional.of(auditService),
new DinaMapper<>(SequenceManagedAttributeDto.class),
SequenceManagedAttributeDto.class,
SequenceManagedAttribute.class,
null,
externalResourceProvider,
buildProperties, objectMapper);
this.dinaService = service;
this.dinaAuthenticatedUser = dinaAuthenticatedUser;
}

@Override
public <S extends SequenceManagedAttributeDto> S create(S resource) {
dinaAuthenticatedUser.ifPresent(
authenticatedUser -> resource.setCreatedBy(authenticatedUser.getUsername()));
return super.create(resource);
}

@Override
public SequenceManagedAttributeDto findOne(Serializable id, QuerySpec querySpec) {

// Allow lookup by component type + key.
// e.g. collecting_event.attribute_name
var matcher = KEY_LOOKUP_PATTERN.matcher(id.toString());
if (matcher.groupCount() == 2) {
if (matcher.find()) {
SequenceManagedAttribute.ManagedAttributeComponent component = SequenceManagedAttribute.ManagedAttributeComponent
.fromString(matcher.group(1));
String attributeKey = matcher.group(2);

SequenceManagedAttribute managedAttribute =
dinaService.findOneByKeyAndComponent(attributeKey, component);

if (managedAttribute != null) {
return getMappingLayer().toDtoSimpleMapping(managedAttribute);
} else {
throw new ResourceNotFoundException("Managed Attribute not found: " + TextHtmlSanitizer.sanitizeText(id.toString()));
}
}
}

return super.findOne(id, querySpec);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package ca.gc.aafc.seqdb.api.security;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

import ca.gc.aafc.dina.security.auth.PermissionAuthorizationService;

/**
* Authorization service that will authorize SUPER_USER (and DINA_ADMIN) for the group of the entity.
* The currentUser must be at least SUPER_USER on the group defined on the entity.
*/
@Service
public class SuperUserInGroupCUDAuthorizationService extends PermissionAuthorizationService {

@Override
@PreAuthorize("hasMinimumGroupAndRolePermissions(@currentUser, 'SUPER_USER', #entity)")
public void authorizeCreate(Object entity) {
}

@Override
@PreAuthorize("hasMinimumGroupAndRolePermissions(@currentUser, 'SUPER_USER', #entity)")
public void authorizeUpdate(Object entity) {
}

@Override
@PreAuthorize("hasMinimumGroupAndRolePermissions(@currentUser, 'SUPER_USER', #entity)")
public void authorizeDelete(Object entity) {
}

// Do nothing for now
@Override
public void authorizeRead(Object entity) {
}

@Override
public String getName() {
return "SuperUserInGroupCUDAuthorizationService";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package ca.gc.aafc.seqdb.api.service;

import liquibase.repackaged.org.apache.commons.lang3.StringUtils;
import lombok.NonNull;

import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Service;
import org.springframework.validation.SmartValidator;

import ca.gc.aafc.dina.jpa.BaseDAO;
import ca.gc.aafc.dina.service.ManagedAttributeService;
import ca.gc.aafc.dina.service.PostgresJsonbService;
import ca.gc.aafc.dina.util.UUIDHelper;
import ca.gc.aafc.seqdb.api.entities.SequenceManagedAttribute;

@Service
public class SequenceManagedAttributeService extends ManagedAttributeService<SequenceManagedAttribute> {

public static final String MANAGED_ATTRIBUTES_COL_NAME = "managed_attributes";

public static final String GENERIC_MOLECULAR_ANALYSIS_TABLE_NAME = "generic_molecular_analysis";

private static final String COMPONENT_FIELD_NAME = "managedAttributeComponent";

private final PostgresJsonbService jsonbService;

public SequenceManagedAttributeService(
@NonNull BaseDAO baseDAO,
@NonNull SmartValidator sv,
@NonNull PostgresJsonbService postgresJsonbService) {
super(baseDAO, sv, SequenceManagedAttribute.class);
this.jsonbService = postgresJsonbService;
}

@Override
protected void preCreate(SequenceManagedAttribute entity) {
entity.setUuid(UUIDHelper.generateUUIDv7());
entity.setGroup(standardizeGroupName(entity));
super.preCreate(entity);
}

@Override
protected void preDelete(SequenceManagedAttribute entity) {
switch (entity.getManagedAttributeComponent()) {
case GENERIC_MOLECULAR_ANALYSIS:
checkKeysFor(entity.getKey(), GENERIC_MOLECULAR_ANALYSIS_TABLE_NAME);
break;
default:
throw new IllegalStateException(
"Unexpected managed attribute component of: " + entity.getManagedAttributeComponent());
}
}

public SequenceManagedAttribute findOneByKeyAndComponent(String key,
SequenceManagedAttribute.ManagedAttributeComponent component) {
if (StringUtils.isBlank(key) || component == null) {
return null;
}
return findOneByKeyAnd(key, Pair.of(COMPONENT_FIELD_NAME, component));
}

private void checkKeysFor(String key, String tableName) {
Integer countFirstLevelKeys = jsonbService.countFirstLevelKeys(
tableName, SequenceManagedAttributeService.MANAGED_ATTRIBUTES_COL_NAME, key);
if (countFirstLevelKeys > 0) {
throw new IllegalStateException("Managed attribute key: " + key + ", is currently in use.");
}
}
}
1 change: 1 addition & 0 deletions src/main/resources/db/changelog/db.changelog-master.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,5 @@
<include file="db/changelog/migrations/52-Add_type_to_MolecularAnalysisRunItem.xml"/>
<include file="db/changelog/migrations/53-Add_attachments_to_MolecularAnalysisRun.xml"/>
<include file="db/changelog/migrations/54-Add_metagenomics_tables.xml"/>
<include file="db/changelog/migrations/55-Add_managed_attribute.xml"/>
</databaseChangeLog>
Loading

0 comments on commit 5386a12

Please sign in to comment.