Skip to content

Commit 1f16578

Browse files
authored
Merge pull request #440 from qbicsoftware/release/0.31.0
Release 0.31.0
2 parents 19dfa40 + 0a341a7 commit 1f16578

File tree

78 files changed

+3771
-2755
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+3771
-2755
lines changed

application-commons/src/main/java/life/qbic/application/commons/ApplicationException.java

+1
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ public enum ErrorCode {
144144
NO_SPECIES_DEFINED,
145145
NO_SPECIMEN_DEFINED,
146146
NO_ANALYTE_DEFINED,
147+
DATA_ATTACHED_TO_SAMPLES
147148
;
148149

149150
@Override

identity-infrastructure/src/main/java/life/qbic/identity/infrastructure/UserJpaRepository.java

-7
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,6 @@ public List<User> findUsersByEmailAddress(EmailAddress emailAddress) {
4343
@Override
4444
public void save(User user) {
4545
userRepo.save(user);
46-
// TODO: we need to broadcast the user registered event and consume it in the project management
47-
// domain, such that need to add the SID entry, which is project management concern
48-
//
49-
// if (!sidRepository.existsBySidEqualsIgnoreCaseAndPrincipalEquals(user.emailAddress().get(),
50-
// true)) {
51-
// addSid(user.id().get(), true);
52-
// }
5346
}
5447

5548
@Override

project-management-infrastructure/src/main/java/life/qbic/projectmanagement/infrastructure/OffsetBasedRequest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ public class OffsetBasedRequest implements Pageable {
2020
private final Sort sort;
2121

2222
/**
23-
* Basic constructor.
23+
* Basic constructor. Using it (and not specifying a Sort option) might lead to slower queries.
24+
* Your mileage may vary.
2425
*
2526
* @param offset the offset of the query result index to start
2627
* @param limit the size of the query result, starting from the offset

project-management-infrastructure/src/main/java/life/qbic/projectmanagement/infrastructure/batch/BatchJpaRepository.java

+40-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@
22

33
import static life.qbic.logging.service.LoggerFactory.logger;
44

5+
import java.util.Collection;
6+
import java.util.Objects;
57
import java.util.Optional;
8+
import java.util.stream.Collectors;
69
import life.qbic.application.commons.Result;
710
import life.qbic.logging.api.Logger;
811
import life.qbic.projectmanagement.application.batch.BatchRegistrationService.ResponseCode;
912
import life.qbic.projectmanagement.domain.model.batch.Batch;
1013
import life.qbic.projectmanagement.domain.model.batch.BatchId;
14+
import life.qbic.projectmanagement.domain.model.experiment.ExperimentId;
15+
import life.qbic.projectmanagement.domain.model.sample.Sample;
1116
import life.qbic.projectmanagement.domain.repository.BatchRepository;
17+
import life.qbic.projectmanagement.infrastructure.sample.QbicSampleRepository;
1218
import org.springframework.beans.factory.annotation.Autowired;
1319
import org.springframework.stereotype.Repository;
1420

@@ -25,10 +31,13 @@ public class BatchJpaRepository implements BatchRepository {
2531
private static final Logger log = logger(BatchJpaRepository.class);
2632

2733
private final QbicBatchRepo qbicBatchRepo;
34+
private final QbicSampleRepository qbicSampleRepository;
2835

2936
@Autowired
30-
public BatchJpaRepository(QbicBatchRepo qbicBatchRepo) {
37+
public BatchJpaRepository(QbicBatchRepo qbicBatchRepo,
38+
QbicSampleRepository qbicSampleRepository) {
3139
this.qbicBatchRepo = qbicBatchRepo;
40+
this.qbicSampleRepository = qbicSampleRepository;
3241
}
3342

3443
@Override
@@ -51,4 +60,34 @@ public Optional<Batch> find(BatchId batchId) {
5160
public Result<Batch, ResponseCode> update(Batch batch) {
5261
return Result.fromValue(this.qbicBatchRepo.save(batch));
5362
}
63+
64+
@Override
65+
public Result<Collection<Batch>, ResponseCode> findBatchesByExperimentId(
66+
ExperimentId experimentId) {
67+
Objects.requireNonNull(experimentId);
68+
Collection<Batch> batches;
69+
try {
70+
Collection<Sample> samples = qbicSampleRepository.findAllByExperimentId(experimentId);
71+
Collection<BatchId> batchIds = samples.stream().map(Sample::assignedBatch)
72+
.collect(Collectors.toSet());
73+
batches = batchIds.stream().map(qbicBatchRepo::findById).filter(
74+
Optional::isPresent).map(Optional::get).toList();
75+
} catch (Exception e) {
76+
log.error(
77+
"Retrieving Batches for experiment with id " + experimentId.value() + " failed: " + e);
78+
return Result.fromError(ResponseCode.BATCHES_COULD_NOT_BE_RETRIEVED);
79+
}
80+
return Result.fromValue(batches);
81+
}
82+
83+
@Override
84+
public Result<BatchId, ResponseCode> deleteById(BatchId batchId) {
85+
try {
86+
qbicBatchRepo.deleteById(batchId);
87+
} catch (Exception e) {
88+
log.error(e.getMessage(), e);
89+
return Result.fromError(ResponseCode.BATCH_DELETION_FAILED);
90+
}
91+
return Result.fromValue(batchId);
92+
}
5493
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package life.qbic.projectmanagement.infrastructure.ontology;
2+
3+
import java.util.List;
4+
import java.util.Objects;
5+
import life.qbic.projectmanagement.application.OntologyClassEntity;
6+
import life.qbic.projectmanagement.application.SortOrder;
7+
import life.qbic.projectmanagement.application.api.OntologyTermLookup;
8+
import life.qbic.projectmanagement.infrastructure.OffsetBasedRequest;
9+
import org.springframework.context.annotation.Scope;
10+
import org.springframework.data.domain.Sort;
11+
import org.springframework.data.domain.Sort.Order;
12+
import org.springframework.stereotype.Component;
13+
14+
/**
15+
* Basic implementation to query ontology class information
16+
*
17+
* @since 1.0.0
18+
*/
19+
@Component
20+
@Scope("singleton")
21+
public class OntologyTermJpaRepository implements OntologyTermLookup {
22+
23+
private final OntologyTermRepository ontologyTermRepository;
24+
25+
public OntologyTermJpaRepository(OntologyTermRepository ontologyTermRepository) {
26+
Objects.requireNonNull(ontologyTermRepository);
27+
this.ontologyTermRepository = ontologyTermRepository;
28+
}
29+
30+
@Override
31+
public List<OntologyClassEntity> query(int offset, int limit) {
32+
return ontologyTermRepository.findAll(new OffsetBasedRequest(offset, limit)).getContent();
33+
}
34+
35+
/**
36+
* The way the MyISAM engine searches the fulltext index makes it necessary to use multiple
37+
* search terms that need to be found instead of one full search term.
38+
* The asterisk suffix is needed so incomplete words are found (mu for musculus).
39+
*/
40+
private String buildSearchTerm(String searchString) {
41+
StringBuilder searchTermBuilder = new StringBuilder();
42+
for(String word : searchString.split(" ")) {
43+
searchTermBuilder.append(" +" + word);
44+
}
45+
searchTermBuilder.append("*");
46+
return searchTermBuilder.toString().trim();
47+
}
48+
49+
@Override
50+
public List<OntologyClassEntity> query(String searchString, List<String> ontologies, int offset,
51+
int limit, List<SortOrder> sortOrders) {
52+
List<Order> orders = sortOrders.stream().map(it -> {
53+
Order order;
54+
if (it.isDescending()) {
55+
order = Order.desc(it.propertyName());
56+
} else {
57+
order = Order.asc(it.propertyName());
58+
}
59+
return order;
60+
}).toList();
61+
// provide a short list of initial values when no search string was provided
62+
if(searchString.isBlank()) {
63+
return ontologyTermRepository.findByLabelNotNullAndOntologyIn(ontologies,
64+
new OffsetBasedRequest(offset, limit, Sort.by(orders))).getContent();
65+
}
66+
// if the search string is shorter than 2, normal matching is faster
67+
if(searchString.length()==1) {
68+
return ontologyTermRepository.findByLabelStartingWithIgnoreCaseAndOntologyIn(searchString, ontologies,
69+
new OffsetBasedRequest(offset, limit, Sort.by(orders))).getContent();
70+
}
71+
// otherwise create a more complex search term for fulltext search
72+
String searchTerm = buildSearchTerm(searchString);
73+
return ontologyTermRepository.findByLabelFulltextMatching(
74+
searchTerm, ontologies, new OffsetBasedRequest(offset, limit, Sort.by(orders))).getContent();
75+
}
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package life.qbic.projectmanagement.infrastructure.ontology;
2+
3+
import java.util.List;
4+
import life.qbic.projectmanagement.application.OntologyClassEntity;
5+
import life.qbic.projectmanagement.infrastructure.OffsetBasedRequest;
6+
import org.springframework.data.domain.Page;
7+
import org.springframework.data.domain.Pageable;
8+
import org.springframework.data.jpa.repository.Query;
9+
import org.springframework.data.repository.PagingAndSortingRepository;
10+
11+
/**
12+
* Simple repository to query concise ontology term information
13+
*
14+
* @since 1.0.0
15+
*/
16+
public interface OntologyTermRepository extends
17+
PagingAndSortingRepository<OntologyClassEntity, Long> {
18+
19+
@Query(value = "SELECT * FROM ontology_classes WHERE MATCH(label) AGAINST(?1 IN BOOLEAN MODE) AND ontology in (?2) ORDER BY length(label);",
20+
countQuery = "SELECT count(*) FROM ontology_classes WHERE MATCH(label) AGAINST(?1 IN BOOLEAN MODE) AND ontology in (?2);",
21+
nativeQuery = true)
22+
Page<OntologyClassEntity> findByLabelFulltextMatching(
23+
String termFilter, List<String> ontology, Pageable pageable);
24+
25+
Page<OntologyClassEntity> findByLabelNotNullAndOntologyIn(List<String> ontologies, Pageable pageable);
26+
27+
Page<OntologyClassEntity> findByLabelStartingWithIgnoreCaseAndOntologyIn(String filter, List<String> ontology, Pageable pageable);
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package life.qbic.projectmanagement.infrastructure.ontology;
2+
3+
import static life.qbic.logging.service.LoggerFactory.logger;
4+
5+
import java.util.List;
6+
import java.util.Optional;
7+
import life.qbic.logging.api.Logger;
8+
import life.qbic.projectmanagement.application.OntologyClassEntity;
9+
import life.qbic.projectmanagement.domain.repository.OntologyTermRepository;
10+
import org.springframework.beans.factory.annotation.Autowired;
11+
import org.springframework.stereotype.Service;
12+
13+
14+
/**
15+
* <b>Ontology term repository implementation</b>
16+
*
17+
* <p>Implementation for the {@link OntologyTermRepository} interface.
18+
*
19+
* <p>This class serves as an adapter and proxies requests to an JPA implementation to interact
20+
* with persistent {@link OntologyClassEntity} data in the storage layer.
21+
*
22+
* <p>The actual JPA implementation is done by {@link QbicOntologyTermRepo}, which is injected as
23+
* dependency upon creation.
24+
* <p>
25+
*
26+
* @since 1.0.0
27+
*/
28+
@Service
29+
public class OntologyTermRepositoryImpl implements OntologyTermRepository {
30+
31+
private static final Logger log = logger(OntologyTermRepositoryImpl.class);
32+
private final QbicOntologyTermRepo ontologyTermRepo;
33+
34+
@Autowired
35+
public OntologyTermRepositoryImpl(QbicOntologyTermRepo ontologyTermRepo) {
36+
this.ontologyTermRepo = ontologyTermRepo;
37+
}
38+
39+
@Override
40+
public List<OntologyClassEntity> find(String name) {
41+
return ontologyTermRepo.findOntologyTermByName(name);
42+
}
43+
44+
@Override
45+
public Optional<OntologyClassEntity> find(Long id) {
46+
return ontologyTermRepo.findById(id);
47+
}
48+
49+
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package life.qbic.projectmanagement.infrastructure.ontology;
2+
3+
import java.util.List;
4+
import life.qbic.projectmanagement.application.OntologyClassEntity;
5+
import org.springframework.data.repository.CrudRepository;
6+
7+
/**
8+
* <b>QBiC ontology term interface</b>
9+
*
10+
* <p>This interface will be automatically detected by Spring on application startup and create an
11+
* instance of this class automatically.
12+
*
13+
* <p>Since it extends the {@link CrudRepository} class from Spring, no need to write queries. The
14+
* framework will do that for us.
15+
*
16+
* @since 1.0.0
17+
*/
18+
public interface QbicOntologyTermRepo extends CrudRepository<OntologyClassEntity, Long> {
19+
20+
List<OntologyClassEntity> findOntologyTermByName(String name);
21+
}

project-management-infrastructure/src/main/java/life/qbic/projectmanagement/infrastructure/sample/QbicSampleDataRepo.java

+7-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package life.qbic.projectmanagement.infrastructure.sample;
22

3+
import java.util.Collection;
34
import java.util.List;
45
import life.qbic.projectmanagement.domain.model.project.Project;
6+
import life.qbic.projectmanagement.domain.model.project.ProjectCode;
57
import life.qbic.projectmanagement.domain.model.sample.Sample;
68
import life.qbic.projectmanagement.domain.model.sample.SampleCode;
79

@@ -26,20 +28,13 @@ public interface QbicSampleDataRepo {
2628
void addSamplesToProject(Project project, List<Sample> samples);
2729

2830
/**
29-
* Deletes a sample with the provided code from persistence.
31+
* Removes the provided samples from persistence
3032
*
31-
* @param sampleCode the {@link SampleCode} of the sample to delete
32-
* @since 1.0.0
33+
* @param projectCode the {@link ProjectCode} of the project these samples belong to
34+
* @param samples the {@link Sample} to be removed from the data repository
3335
*/
34-
void delete(SampleCode sampleCode);
36+
void deleteAll(ProjectCode projectCode, Collection<SampleCode> samples);
3537

36-
/**
37-
* Searches for samples that contain the provided sample code
38-
*
39-
* @param sampleCode the {@link SampleCode} to search for in the data repository
40-
* @return true, if a sample with that code already exists in the system, false if not
41-
* @since 1.0.0
42-
*/
43-
boolean sampleExists(SampleCode sampleCode);
38+
void updateAll(Collection<Sample> samples);
4439

4540
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package life.qbic.projectmanagement.infrastructure.sample;
22

33
import java.util.Collection;
4+
import java.util.List;
5+
import life.qbic.projectmanagement.domain.model.batch.BatchId;
46
import life.qbic.projectmanagement.domain.model.experiment.ExperimentId;
57
import life.qbic.projectmanagement.domain.model.sample.Sample;
68
import life.qbic.projectmanagement.domain.model.sample.SampleId;
79
import org.springframework.data.jpa.repository.JpaRepository;
10+
811
public interface QbicSampleRepository extends JpaRepository<Sample, SampleId> {
912

10-
Collection<Sample> findAllByExperimentId(ExperimentId experimentId);
13+
Collection<Sample> findAllByExperimentId(ExperimentId experimentId);
14+
15+
List<Sample> findAllByAssignedBatch(BatchId batchId);
1116
}

0 commit comments

Comments
 (0)