Skip to content

Commit a7bc25d

Browse files
authored
Merge pull request #1294 from qbicsoftware/development
Release PR
2 parents efb784d + ef90b7e commit a7bc25d

File tree

149 files changed

+7193
-3824
lines changed

Some content is hidden

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

149 files changed

+7193
-3824
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ ready to be deployed. The file can be found in the `target` folder after the bui
7070
Once the JAR file is built, you can run it using
7171
`java -jar target/datamanager-1.0-SNAPSHOT.jar`
7272

73+
## Maven Profiles
74+
75+
To have all beans available for running the datamanager, you need to set a profile during
76+
compilation.
77+
If you plan to deploy it in production or with external systems integrated, use the `production`
78+
profile.
79+
80+
For local development you can use the `development` profile. This profile will disable and mock
81+
integration with the TIB service, OpenBis and the ROR repository.
82+
7383
### Configuration
7484

7585
#### Java Version

identity/src/main/java/life/qbic/identity/application/user/IdentityService.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,22 @@ private Result<EncryptedPassword, RuntimeException> attemptPasswordEncryption(
274274
}
275275
}
276276

277+
public void setOidc(String userId, String oidcId, String oidcIssuer) throws IssueOidcException {
278+
var alreadyConnectedUser = userRepository.findByOidc(oidcId, oidcIssuer);
279+
if (alreadyConnectedUser.isPresent()) {
280+
throw new IssueOidcException("OpenID already in use.");
281+
}
282+
var user = userRepository.findById(UserId.from(userId)).orElseThrow(() -> new IssueOidcException("User not found"));
283+
user.setOidcEntry(oidcIssuer, oidcId);
284+
userRepository.updateUser(user);
285+
}
286+
287+
public static class IssueOidcException extends RuntimeException {
288+
public IssueOidcException(String message) {
289+
super(message);
290+
}
291+
}
292+
277293
public static class EmptyUserNameException extends ApplicationException {
278294

279295
@Serial

identity/src/main/java/life/qbic/identity/domain/model/User.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,17 @@ public static User create(FullName fullName, EmailAddress emailAddress,
102102
return user;
103103
}
104104

105+
/**
106+
* Sets the OIDC issue and identifier for the user. Overwrites existing entries.
107+
* @param oidcIssuer the issuer of the OIDC principal
108+
* @param oidcId the identifier of the OIDC principal
109+
* @since 1.11.0
110+
*/
111+
public void setOidcEntry(String oidcIssuer, String oidcId) {
112+
this.oidcIssuer = oidcIssuer;
113+
this.oidcId = oidcId;
114+
}
115+
105116
public static User createOidc(String fullName, String emailAddress,
106117
String userName, String oidcIssuer, String oidcId) {
107118
if (isNull(oidcIssuer) && nonNull(oidcId)) {

project-management-infrastructure/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@
142142
<artifactId>poi-ooxml</artifactId>
143143
<version>5.4.0</version>
144144
</dependency>
145+
<dependency>
146+
<groupId>org.springframework.boot</groupId>
147+
<artifactId>spring-boot-autoconfigure</artifactId>
148+
</dependency>
145149
</dependencies>
146150

147151
</project>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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.ontology.LookupException;
9+
import life.qbic.projectmanagement.application.ontology.OntologyClass;
10+
import life.qbic.projectmanagement.application.ontology.TerminologySelect;
11+
import org.springframework.context.annotation.Profile;
12+
import org.springframework.stereotype.Service;
13+
14+
@Service
15+
@Profile("development")
16+
public class MockTerminologySelect implements TerminologySelect {
17+
18+
private static final Logger log = logger(MockTerminologySelect.class);
19+
20+
private static void logWarning() {
21+
log.warn("Using mock implementation. Not suited for production deployment.");
22+
}
23+
24+
@Override
25+
public List<OntologyClass> query(String searchTerm, int offset, int limit)
26+
throws LookupException {
27+
logWarning();
28+
return List.of();
29+
}
30+
31+
@Override
32+
public Optional<OntologyClass> searchByCurie(String curie) throws LookupException {
33+
logWarning();
34+
return Optional.empty();
35+
}
36+
37+
@Override
38+
public List<OntologyClass> search(String searchTerm, int offset, int limit)
39+
throws LookupException {
40+
logWarning();
41+
return List.of();
42+
}
43+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package life.qbic.projectmanagement.infrastructure.ontology;
2+
3+
import java.time.Instant;
4+
import java.util.ArrayList;
5+
import java.util.Comparator;
6+
import java.util.List;
7+
import java.util.Objects;
8+
import java.util.Optional;
9+
import java.util.stream.Collectors;
10+
11+
/**
12+
* <b>Request cache</b>
13+
*
14+
* <p>In memory look-up cache for {@link TibTerm}.</p>
15+
* <p>
16+
* If you need thread-safety, use {@link SynchronizedRequestCache}.
17+
*
18+
* @since 1.11.0
19+
*/
20+
public class RequestCache {
21+
22+
private final List<TibTerm> cache = new ArrayList<>();
23+
private final int limit;
24+
private List<CacheEntryStat> accessFrequency = new ArrayList<>();
25+
26+
RequestCache(int limit) {
27+
this.limit = limit;
28+
}
29+
30+
/**
31+
* Adds a {@link TibTerm} to the in-memory cache.
32+
* <p>
33+
* If the cache max size is reached, the oldest entry will be replaced with the one passed to the
34+
* function.
35+
*
36+
* @param term the term to store in the cache
37+
* @since 1.9.0
38+
*/
39+
void add(TibTerm term) {
40+
if (term == null || cache.contains(term)) return;
41+
if (cache.size() >= limit) {
42+
addByReplace(term);
43+
return;
44+
}
45+
cache.add(term);
46+
addStats(new CacheEntryStat(term));
47+
}
48+
49+
// Puts the term with the time of caching into an own list for tracking
50+
private void addStats(CacheEntryStat cacheEntryStat) {
51+
if (accessFrequency.contains(cacheEntryStat)) {
52+
return;
53+
}
54+
accessFrequency.add(cacheEntryStat);
55+
}
56+
57+
// A special case of adding by looking for the oldest cache entry and replacing it with
58+
// the provided one
59+
private void addByReplace(TibTerm term) {
60+
// We want to be sure that the access statistic list is in natural order
61+
ensureSorted();
62+
// We then remove the oldest cache entry
63+
if (!cache.isEmpty()) {
64+
cache.set(0, term);
65+
addStats(new CacheEntryStat(term));
66+
}
67+
}
68+
69+
// Ensures the natural order sorting by datetime, when the cache entry has been created
70+
// Oldest entry will be the first element, newest the last element of the list
71+
private void ensureSorted() {
72+
accessFrequency = accessFrequency.stream()
73+
.sorted(Comparator.comparing(CacheEntryStat::created, Instant::compareTo))
74+
.collect(Collectors.toList());
75+
}
76+
77+
/**
78+
* Searches for a matching {@link TibTerm} in the cache.
79+
*
80+
* @param curie the CURIE to search for
81+
* @return the search result, {@link Optional#empty()} if no match was found
82+
* @since 1.9.0
83+
*/
84+
Optional<TibTerm> findByCurie(String curie) {
85+
return cache.stream().filter(term -> term.oboId.equals(curie)).findFirst();
86+
}
87+
88+
89+
/**
90+
* A small container for when a cache entry has been created.
91+
*
92+
* @since 1.9.0
93+
*/
94+
private static class CacheEntryStat {
95+
96+
private final TibTerm term;
97+
private final Instant created;
98+
99+
CacheEntryStat(TibTerm term) {
100+
this.term = term;
101+
created = Instant.now();
102+
}
103+
104+
/**
105+
* When the cache entry has been created
106+
*
107+
* @return the instant of creation
108+
* @since 1.9.0
109+
*/
110+
Instant created() {
111+
return created;
112+
}
113+
114+
@Override
115+
public boolean equals(Object o) {
116+
if (o == null || getClass() != o.getClass()) {
117+
return false;
118+
}
119+
CacheEntryStat that = (CacheEntryStat) o;
120+
return Objects.equals(term, that.term);
121+
}
122+
123+
@Override
124+
public int hashCode() {
125+
return Objects.hashCode(term);
126+
}
127+
}
128+
129+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package life.qbic.projectmanagement.infrastructure.ontology;
2+
3+
import java.util.Objects;
4+
import java.util.Optional;
5+
import org.springframework.beans.factory.annotation.Value;
6+
import org.springframework.stereotype.Component;
7+
8+
/**
9+
* Thread-safe implementation of {@link RequestCache}.
10+
*
11+
* @since 1.11.0
12+
*/
13+
@Component
14+
public class SynchronizedRequestCache {
15+
16+
private final RequestCache cache;
17+
18+
private final Object lock = new Object();
19+
20+
public SynchronizedRequestCache(@Value("${terminology.service.cache.size}") String cacheSize) {
21+
cache = new RequestCache(Integer.parseInt(cacheSize));
22+
}
23+
24+
/**
25+
* Adds a {@link TibTerm} to the in-memory cache.
26+
* <p>
27+
* If the cache max size is reached, the oldest entry will be replaced with the one passed to the
28+
* function.
29+
* <p>
30+
* This function is thread-safe.
31+
*
32+
* @param term the term to store in the cache
33+
* @since 1.11.0
34+
*/
35+
public void add(TibTerm term) {
36+
synchronized (lock) {
37+
cache.add(term);
38+
}
39+
}
40+
41+
/**
42+
* Searches for a matching {@link TibTerm} in the cache.
43+
* <p>
44+
* This function is thread-safe.
45+
*
46+
* @param curie the CURIE to search for
47+
* @return the search result, {@link Optional#empty()} if no match was found
48+
* @since 1.11.0
49+
*/
50+
Optional<TibTerm> findByCurie(String curie) {
51+
Objects.requireNonNull(curie);
52+
synchronized (lock) {
53+
return cache.findByCurie(curie);
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)