Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
20e4420
Upgrade spring boot from 3.5.9 to 3.5.14
KochTobi May 27, 2026
668f861
Remove deprecated class from SecurityConfiguration.java
KochTobi May 27, 2026
ef194c2
Remove use of deprecated getOboId method
KochTobi May 27, 2026
05abdbb
Replace deprecated StreamResource for images
KochTobi May 27, 2026
b575d1e
Replace deprecated findSample method
KochTobi May 27, 2026
11618ca
Replace deprecated Vaadin stream download with DownloadHandler
KochTobi May 28, 2026
c61e894
Remove deprecated plugin option
KochTobi May 28, 2026
9099231
Address code warnings and remove dead code
KochTobi May 28, 2026
8a6f056
Replace deprecated method
KochTobi May 28, 2026
30fe6a1
Remove unused code
KochTobi May 28, 2026
57e8692
Introduce new SmartUploadComponent
KochTobi May 29, 2026
438b5db
Fix isEmpty method
KochTobi May 29, 2026
da63b11
Fix UploadConfiguration
KochTobi May 29, 2026
e8b19c9
Add upload component to component demo
KochTobi May 29, 2026
3211495
feat(upload): add UploadFileDisplay and UploadContentValidator interf…
KochTobi-Agent May 29, 2026
05d32ea
Add change type to interface
KochTobi May 29, 2026
844ee58
docs(upload): add Javadoc to UploadFileDisplay and UploadContentValid…
KochTobi-Agent May 29, 2026
d18f2f2
Add multi-file functionality to SmartUploadComponent.java
KochTobi Jun 1, 2026
a345b87
Continue on SmartUploadComponent.java
KochTobi Jun 1, 2026
5e0e245
Rename SmartUploadComponent.java to ContentUploadComponent.java
KochTobi Jun 1, 2026
86bbe64
Move validation out of upload component
KochTobi Jun 9, 2026
8c0f708
Address code warnings
KochTobi Jun 9, 2026
302a1d9
Fix wrong html tree for display
KochTobi Jun 9, 2026
99359df
Move restrictions display to the Upload component
KochTobi Jun 9, 2026
2d9184b
Fix restriction positioning
KochTobi Jun 10, 2026
3668b6f
Display file rejections
KochTobi Jun 10, 2026
cf0392e
add fixme comment to be tackled in vaadin25.1
KochTobi Jun 10, 2026
b8c4188
Migrate EditSampleBatchDialog to ContentUploadComponent (Vaadin 25)
KochTobi-Agent Jun 10, 2026
961ad93
Revert "Migrate EditSampleBatchDialog to ContentUploadComponent (Vaad…
KochTobi Jun 10, 2026
2aa8a24
extract SampleUploadDisplay.java
KochTobi Jun 12, 2026
1cae2c2
Use vaadin25 upload for EditSampleBatchDialog.java
KochTobi Jun 12, 2026
f944393
Fix constructor parameters
KochTobi Jun 12, 2026
5454c7f
Fix wrong files removed
KochTobi Jun 12, 2026
79ed4a6
Fix parallelism in CompletableFuture
KochTobi Jun 12, 2026
2865c53
Switch FileSizeFormatter to 1024
KochTobi Jun 12, 2026
6301e32
Migrate UploadPurchaseDialog.java to Vaadin25 upload
KochTobi Jun 12, 2026
9bc902a
Migrate QualityControlUpload to new upload process
KochTobi Jun 12, 2026
03b76de
Fix test
KochTobi Jun 15, 2026
1310681
Add JavaDoc to ContentUploadComponent
KochTobi-Agent Jun 15, 2026
ed7fdf9
Merge pull request #1453 from qbicsoftware/dependencies/upgrade-sprin…
KochTobi Jun 15, 2026
40a37fb
Merge branch 'main' into dependencies/upgrade-spring-boot
KochTobi Jun 15, 2026
0c1e855
Merge branch 'main' into dependencies/upgrade-spring-boot
KochTobi Jun 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
*/
public class FileSizeFormatter {

private static final long KILOBYTE = 1_000L;
private static final long MEGABYTE = 1_000_000L;
private static final long GIGABYTE = 1_000_000_000L;
private static final long base = 1024;

Check failure on line 14 in application-commons/src/main/java/life/qbic/application/commons/FileSizeFormatter.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this constant name to match the regular expression '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$'.

See more on https://sonarcloud.io/project/issues?id=qbicsoftware_data-manager-app&issues=AZ7LZ7duMamo7nX0iKms&open=AZ7LZ7duMamo7nX0iKms&pullRequest=1454
private static final long KILOBYTE = base;
private static final long MEGABYTE = base * KILOBYTE;
private static final long GIGABYTE = base * MEGABYTE;

private FileSizeFormatter() {
// Utility class - no instantiation
Expand Down Expand Up @@ -43,4 +44,4 @@
}
return String.format(Locale.US, "%.2f GB", bytes / (double) GIGABYTE);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,16 @@ Since we want to remove the spacing between the cancel and confirm button we rep
width: 100%;
}

.upload-component .restrictions:has(:only-child) {
justify-content: flex-end;
}

.upload-component .upload-component-error {
font-size: smaller;
color: var(--lumo-error-color);
padding: var(--lumo-space-xs);
}

.measurement-upload-dialog .upload-items-display .uploaded-items-section .section-title {
color: var(--lumo-secondary-text-color);
font-size: var(--lumo-font-size-s);
Expand All @@ -514,8 +524,7 @@ Since we want to remove the spacing between the cancel and confirm button we rep
}

.measurement-upload-dialog .upload-items-display .uploaded-measurement-items .measurement-item,
.register-samples-dialog .uploaded-items-section .uploaded-item,
.edit-samples-dialog .uploaded-items-section .uploaded-item {
.uploaded-items-section .uploaded-item {
font-size: var(--lumo-font-size-s);
display: flex;
padding-top: var(--lumo-space-m);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package life.qbic.datamanager.configuration;

import java.util.Objects;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.unit.DataSize;

@Configuration
public class UploadConfiguration {

private final DataSize maxInMemoryThreshold;
private final DataSize maxFileSize;

public UploadConfiguration(
@Value("${qbic.upload.in-memory-limit}") DataSize maxInMemoryThreshold,
@Value("${qbic.upload.max-file-size}") DataSize maxFileSize) {
this.maxInMemoryThreshold = Objects.requireNonNull(maxInMemoryThreshold);
this.maxFileSize = Objects.requireNonNull(maxFileSize);
}

public DataSize maxInMemoryThreshold() {
return maxInMemoryThreshold;
}

public DataSize maxFileSize() {
return maxFileSize;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ public interface ByteArrayDownloadStreamProvider extends DownloadStreamProvider
default InputStream getStream() {
return new ByteArrayInputStream(getBytes());
}

@Override
default String getContentType() {
return "application/octet-stream";
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package life.qbic.datamanager.files.export.download;

import java.io.InputStream;
import java.util.Optional;

public interface DownloadStreamProvider {

String getFilename();

InputStream getStream();

String getContentType();

Optional<Long> contentLength();
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ default InputStream getStream() {
throw new RuntimeException(e);
}
}

default String getContentType() {
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package life.qbic.datamanager.security;

import static com.vaadin.flow.spring.security.VaadinSecurityConfigurer.vaadin;
import static java.util.Objects.requireNonNull;

import com.vaadin.flow.spring.security.VaadinAwareSecurityContextHolderStrategyConfiguration;
import com.vaadin.flow.spring.security.VaadinDefaultRequestCache;
import com.vaadin.flow.spring.security.VaadinWebSecurity;
import life.qbic.datamanager.views.login.LoginLayout;
import life.qbic.identity.application.security.QBiCPasswordEncoder;
import life.qbic.identity.application.user.IdentityService;
Expand All @@ -16,13 +16,13 @@
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@EnableWebSecurity
@Configuration
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
public class SecurityConfiguration extends VaadinWebSecurity {
public class SecurityConfiguration {

final VaadinDefaultRequestCache defaultRequestCache;
private final IdentityService identityService;
Expand All @@ -44,6 +44,30 @@ public SecurityConfiguration(
this.identityService = requireNonNull(identityService);
}

@Bean
public SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http) throws Exception {
http.requestCache(c -> c.requestCache(defaultRequestCache));

http.authorizeHttpRequests(v -> v
.requestMatchers(
"/oauth2/authorization/orcid",
"/oauth2/code/**",
"/link/**",
"/images/*.png")
.permitAll()
);

http.oauth2Login(oauth2 -> oauth2.
loginPage("/login").permitAll()
.defaultSuccessUrl("/")
.successHandler(authenticationSuccessHandler())
.failureUrl("/login?errorOauth2=true&error"));

http.with(vaadin(), vaadin -> vaadin
.loginView(LoginLayout.class, contextPath + "/login?logout=true"));
return http.build();
}

@Bean
public PasswordEncoder passwordEncoder() {
return new QBiCPasswordEncoder();
Expand All @@ -56,34 +80,4 @@ private AuthenticationSuccessHandler authenticationSuccessHandler() {
storedRequestAwareOidcAuthenticationSuccessHandler.setRequestCache(defaultRequestCache);
return storedRequestAwareOidcAuthenticationSuccessHandler;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
// Use Vaadin’s request cache (so redirects back to the original Flow route work)
http.requestCache(c -> c.requestCache(defaultRequestCache));

http.authorizeHttpRequests(v -> v
.requestMatchers(VaadinWebSecurity.getDefaultWebSecurityIgnoreMatcher()).permitAll()
.requestMatchers(
new AntPathRequestMatcher("/oauth2/authorization/orcid"),
new AntPathRequestMatcher("/oauth2/code/**"),
new AntPathRequestMatcher("/link/**"),
new AntPathRequestMatcher("/images/*.png"))
.permitAll()
);

http.oauth2Login(oAuth2Login -> {
oAuth2Login.loginPage("/login").permitAll();
oAuth2Login.defaultSuccessUrl("/");
oAuth2Login.successHandler(
authenticationSuccessHandler());
oAuth2Login.failureUrl("/login?errorOauth2=true&error");
});

// Let Vaadin register its filters/matchers
super.configure(http);

// Set the login view
setLoginView(http, LoginLayout.class, contextPath + "/login?logout=true");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.server.streams.DownloadHandler;
import com.vaadin.flow.server.streams.DownloadResponse;

/**
* Landing Page Title and Logo
Expand Down Expand Up @@ -33,9 +34,11 @@ public LandingPageTitleAndLogo() {
}

private Image getUTLogo() {
StreamResource utResource = new StreamResource("university_tuebingen_logo.svg",
() -> getClass().getClassLoader().getResourceAsStream(UT_LOGO_PATH));
Image utLogo = new Image(utResource, "university_tuebingen_logo");
DownloadHandler downloadHandler = DownloadHandler.fromInputStream(event ->
new DownloadResponse(getClass().getClassLoader().getResourceAsStream(UT_LOGO_PATH),
"university_tuebingen_logo.svg",
"image/svg+xml", -1));
Image utLogo = new Image(downloadHandler, "university_tuebingen_logo");
utLogo.addClassName("ut-logo");
return utLogo;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import com.vaadin.flow.component.avatar.Avatar;
import com.vaadin.flow.component.avatar.AvatarGroup;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.server.streams.DownloadHandler;
import com.vaadin.flow.server.streams.DownloadResponse;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import life.qbic.datamanager.views.identicon.IdenticonGenerator;
Expand All @@ -19,20 +20,26 @@ public UserAvatar() {
}

public void setUserId(String userId) {
setImageResource(getImageResource(userId));
setImageHandler(getDownloadHandler(userId));
}

private static StreamResource getImageResource(String name) {
return new StreamResource("user-identicon.svg",
() -> new ByteArrayInputStream(IdenticonGenerator.generateIdenticonSVG(name).getBytes(
StandardCharsets.UTF_8)));
private static DownloadHandler getDownloadHandler(String userid) {
return DownloadHandler.fromInputStream(event ->
{
byte[] imageBytes = IdenticonGenerator.generateIdenticonSVG(userid).getBytes(
StandardCharsets.UTF_8);
return new DownloadResponse(
new ByteArrayInputStream(imageBytes),
"user-identicon.svg", null, imageBytes.length);
});
}

public static class UserAvatarGroupItem extends AvatarGroup.AvatarGroupItem {

public UserAvatarGroupItem(String userName, String userId) {
//new logic -> first more important; image then name -> image; name then image -> name is shown
setImageHandler(UserAvatar.getDownloadHandler(userId));
super.setName(userName);
setImageResource(UserAvatar.getImageResource(userId));
}
}
}
Loading
Loading