Skip to content

Commit

Permalink
Merge pull request #331 from qbicsoftware/development
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
KochTobi authored Jul 25, 2023
2 parents b0e9f16 + 2e0d672 commit 27bc600
Show file tree
Hide file tree
Showing 70 changed files with 2,064 additions and 1,526 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,87 +16,19 @@
*/
public class ApplicationException extends RuntimeException {

/**
* The error code of an ApplicationException. This code can be used to determine what kind of
* error occurred. This allows for the message of the ApplicationException to hold debug
* information and avoids string matching to search for the error message.
*/
public enum ErrorCode {
GENERAL,
INVALID_EXPERIMENTAL_DESIGN,
INVALID_PROJECT_OBJECTIVE,
INVALID_PROJECT_TITLE,
INVALID_PROJECT_CODE,
DUPLICATE_PROJECT_CODE,
UNDEFINED_VARIABLE_LEVEL,
NO_SPECIES_DEFINED,
NO_SPECIMEN_DEFINED,
NO_ANALYTE_DEFINED,
;

@Override
public String toString() {
return this.getClass().getSimpleName() + "." + this.name();
}

}

/**
* Error parameters to be used in error messages.
*
* @param value an ordered array of parameters
*/
public record ErrorParameters(Object[] value) {

public static ErrorParameters create() {
return new ErrorParameters(new Object[]{});
}

public static ErrorParameters of(Object... parameters) {
return new ErrorParameters(parameters);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

ErrorParameters that = (ErrorParameters) o;

return Arrays.equals(value, that.value);
}

@Override
public int hashCode() {
return Arrays.hashCode(value);
}

@Override
public String toString() {
return new StringJoiner(", ", ErrorParameters.class.getSimpleName() + "[", "]")
.add("value=" + Arrays.toString(value))
.toString();
}
}

private final ErrorCode errorCode;

private final ErrorParameters errorParameters;

public ApplicationException() {
this(ErrorCode.GENERAL, ErrorParameters.create());
this(ErrorCode.GENERAL, ErrorParameters.empty());
}

public ApplicationException(String message) {
this(message, ErrorCode.GENERAL, ErrorParameters.create());
this(message, ErrorCode.GENERAL, ErrorParameters.empty());
}

public ApplicationException(String message, Throwable cause) {
this(message, cause, ErrorCode.GENERAL, ErrorParameters.create());
this(message, cause, ErrorCode.GENERAL, ErrorParameters.empty());
}

public ApplicationException(ErrorCode errorCode, ErrorParameters errorParameters) {
Expand Down Expand Up @@ -132,14 +64,6 @@ public ApplicationException(String message, Throwable cause, boolean enableSuppr
this.errorParameters = errorParameters;
}

public ErrorCode errorCode() {
return errorCode;
}

public ErrorParameters errorParameters() {
return errorParameters;
}

/**
* Wraps the provided throwable into an ApplicationException and sets the provided error message.
* <p>
Expand Down Expand Up @@ -185,6 +109,14 @@ public static ApplicationException wrapping(Throwable e) {
}
}

public ErrorCode errorCode() {
return errorCode;
}

public ErrorParameters errorParameters() {
return errorParameters;
}

@Override
public String toString() {
return new StringJoiner(", ", ApplicationException.class.getSimpleName() + "[", "]")
Expand All @@ -193,4 +125,84 @@ public String toString() {
.add("errorParameters=" + errorParameters)
.toString();
}

/**
* The error code of an ApplicationException. This code can be used to determine what kind of
* error occurred. This allows for the message of the ApplicationException to hold debug
* information and avoids string matching to search for the error message.
*/
public enum ErrorCode {
GENERAL,
INVALID_EXPERIMENTAL_DESIGN,
INVALID_PROJECT_OBJECTIVE,
INVALID_PROJECT_TITLE,
INVALID_PROJECT_CODE,
DUPLICATE_PROJECT_CODE,
UNDEFINED_VARIABLE_LEVEL,
NO_SPECIES_DEFINED,
NO_SPECIMEN_DEFINED,
NO_ANALYTE_DEFINED,
;

@Override
public String toString() {
return this.getClass().getSimpleName() + "." + this.name();
}

}

/**
* Error parameters to be used in error messages.
*
* @param value an ordered array of parameters
*/
public record ErrorParameters(Object[] value) {

/**
* Creates a new instance of an empty error parameter array
*
* @return
* @since 1.0.0
*/
public static ErrorParameters empty() {
return new ErrorParameters(new Object[]{});
}

/**
* Creates a new instance with the provided error parameters
*
* @param parameters the error parameters
* @return
* @since 1.0.0
*/
public static ErrorParameters of(Object... parameters) {
return new ErrorParameters(parameters);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

ErrorParameters that = (ErrorParameters) o;

return Arrays.equals(value, that.value);
}

@Override
public int hashCode() {
return Arrays.hashCode(value);
}

@Override
public String toString() {
return new StringJoiner(", ", ErrorParameters.class.getSimpleName() + "[", "]")
.add("value=" + Arrays.toString(value))
.toString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public Result<Collection<Sample>, SampleInformationService.ResponseCode> findSam
} catch (Exception e) {
log.error(
"Retrieving Samples for experiment with id " + experimentId.value() + " failed: " + e);
return Result.fromError(SampleInformationService.ResponseCode.SAMPLES_NOT_FOUND);
return Result.fromError(SampleInformationService.ResponseCode.QUERY_FAILED);
}
return Result.fromValue(samples);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package life.qbic.projectmanagement.experiment.persistence;

import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.Predicate;
import java.util.List;
import java.util.Objects;
import life.qbic.persistence.OffsetBasedRequest;
import life.qbic.projectmanagement.application.SortOrder;
import life.qbic.projectmanagement.application.sample.SamplePreview;
import life.qbic.projectmanagement.application.sample.SamplePreviewLookup;
import life.qbic.projectmanagement.domain.project.experiment.ExperimentId;
import org.eclipse.jetty.util.StringUtil;
import org.springframework.context.annotation.Scope;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Component;

/**
Expand All @@ -30,7 +35,7 @@ public SamplePreviewJpaRepository(SamplePreviewRepository samplePreviewRepositor

@Override
public List<SamplePreview> queryByExperimentId(ExperimentId experimentId, int offset, int limit,
List<SortOrder> sortOrders) {
List<SortOrder> sortOrders, String filter) {
List<Order> orders = sortOrders.stream().map(it -> {
Order order;
if (it.isDescending()) {
Expand All @@ -40,13 +45,133 @@ public List<SamplePreview> queryByExperimentId(ExperimentId experimentId, int of
}
return order;
}).toList();
return samplePreviewRepository.findSamplePreviewByExperimentId(experimentId,

Specification<SamplePreview> filterSpecification = generateExperimentIdandFilterSpecification(
experimentId, filter);
return samplePreviewRepository.findAll(filterSpecification,
new OffsetBasedRequest(offset, limit, Sort.by(orders))).getContent();
}

@Override
public int queryCountByExperimentId(ExperimentId experimentId) {
return samplePreviewRepository.countSamplePreviewsByExperimentId(experimentId);
public int queryCountByExperimentId(ExperimentId experimentId, String filter) {
Specification<SamplePreview> filterSpecification = generateExperimentIdandFilterSpecification(
experimentId, filter);
return (int) samplePreviewRepository.count(filterSpecification);
}

private Specification<SamplePreview> generateExperimentIdandFilterSpecification(
ExperimentId experimentId, String filter) {
Specification<SamplePreview> isBlankSpec = SamplePreviewSpecs.isBlank(filter);
Specification<SamplePreview> experimentIdSpec = SamplePreviewSpecs.experimentIdEquals(
experimentId);
Specification<SamplePreview> sampleCodeSpec = SamplePreviewSpecs.sampleCodeContains(filter);
Specification<SamplePreview> sampleLabelSpec = SamplePreviewSpecs.sampleLabelContains(filter);
Specification<SamplePreview> batchLabelSpec = SamplePreviewSpecs.batchLabelContains(filter);
Specification<SamplePreview> bioReplicateLabelSpec = SamplePreviewSpecs.bioReplicateLabelContains(
filter);
Specification<SamplePreview> conditionSpec = SamplePreviewSpecs.conditionContains(filter);
Specification<SamplePreview> speciesSpec = SamplePreviewSpecs.speciesContains(filter);
Specification<SamplePreview> specimenSpec = SamplePreviewSpecs.specimenContains(filter);
Specification<SamplePreview> analyteSpec = SamplePreviewSpecs.analyteContains(filter);
Specification<SamplePreview> analysisTypeSpec = SamplePreviewSpecs.analysisTypeContains(filter);
Specification<SamplePreview> commentSpec = SamplePreviewSpecs.commentContains(filter);
Specification<SamplePreview> containsFilterSpec = Specification.anyOf(sampleCodeSpec,
sampleLabelSpec, batchLabelSpec, bioReplicateLabelSpec, conditionSpec, speciesSpec,
specimenSpec, analyteSpec, analysisTypeSpec, commentSpec);
Specification<SamplePreview> isDistinctSpec = SamplePreviewSpecs.isDistinct();
return Specification.where(experimentIdSpec).and(isBlankSpec)
.and(containsFilterSpec)
.and(isDistinctSpec);
}

private static class SamplePreviewSpecs {

//We need to ensure that we only count and retrieve unique samplePreviews
public static Specification<SamplePreview> isDistinct() {
return (root, query, builder) -> {
query.distinct(true);
return null;
};
}

//If no filter was provided return all SamplePreviews
public static Specification<SamplePreview> isBlank(String filter) {
return (root, query, builder) -> {
if (StringUtil.isBlank(filter)) {
return builder.conjunction();
}
return null;
};
}

public static Specification<SamplePreview> experimentIdEquals(ExperimentId experimentId) {
return (root, query, builder) ->
StringUtil.isBlank(experimentId.value()) ?
builder.conjunction() :
builder.equal(root.get("experimentId"), experimentId);
}

public static Specification<SamplePreview> sampleCodeContains(String filter) {
return (root, query, builder) ->
builder.like(root.get("sampleCode"), "%" + filter + "%");
}


public static Specification<SamplePreview> batchLabelContains(String filter) {
return (root, query, builder) ->
builder.like(root.get("batchLabel"), "%" + filter + "%");
}

public static Specification<SamplePreview> bioReplicateLabelContains(String filter) {
return (root, query, builder) ->
builder.like(root.get("bioReplicateLabel"), "%" + filter + "%");
}

public static Specification<SamplePreview> sampleLabelContains(String filter) {
return (root, query, builder) ->
builder.like(root.get("sampleLabel"), "%" + filter + "%");
}

public static Specification<SamplePreview> conditionContains(String filter) {
return (root, query, builder) -> {
Join<?, ?> expVariablesJoin = root.join("experimentalGroup").join("condition")
.join("variableLevels");
Expression<String> varNameExp = expVariablesJoin.get("variableName").as(String.class);
Expression<String> varValueValueExp = expVariablesJoin.get("experimentalValue").get("value")
.as(String.class);
Expression<String> varUnitExp = expVariablesJoin.get("experimentalValue").get("unit")
.as(String.class);
Predicate varValuePred = builder.like(varValueValueExp, "%" + filter + "%");
Predicate varUnitPred = builder.like(varUnitExp, "%" + filter + "%");
Predicate varNamePred = builder.like(varNameExp, "%" + filter + "%");
return builder.or(varNamePred, varValuePred, varUnitPred);
};
}

public static Specification<SamplePreview> speciesContains(String filter) {
return (root, query, builder) ->
builder.like(root.get("species"), "%" + filter + "%");
}

public static Specification<SamplePreview> specimenContains(String filter) {
return (root, query, builder) ->
builder.like(root.get("specimen"), "%" + filter + "%");
}

public static Specification<SamplePreview> analyteContains(String filter) {
return (root, query, builder) ->
builder.like(root.get("analyte"), "%" + filter + "%");
}

public static Specification<SamplePreview> analysisTypeContains(String filter) {
return (root, query, builder) ->
builder.like(root.get("analysisType"), "%" + filter + "%");
}

public static Specification<SamplePreview> commentContains(String filter) {
return (root, query, builder) ->
builder.like(root.get("comment"), "%" + filter + "%");
}

}
}
Loading

0 comments on commit 27bc600

Please sign in to comment.