diff --git a/.gitignore b/.gitignore index a4397c6105..8393ae6082 100755 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,5 @@ fitnesse/target/ daisy-integration/target/ war/target/ api/target/ +test-data/target/ node_modules/ diff --git a/Dockerfile b/Dockerfile index ac53fd07f3..acf3c889a3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,12 +12,14 @@ COPY core/pom.xml core/pom.xml COPY view/pom.xml view/pom.xml COPY war/pom.xml war/pom.xml COPY daisy-integration/pom.xml daisy-integration/pom.xml +COPY test-data/pom.xml test-data/pom.xml COPY api/src/ api/src/ COPY core/src/ core/src/ COPY view/src/ view/src/ COPY war/src/ war/src/ COPY daisy-integration/src/ daisy-integration/src/ +COPY test-data/src/ test-data/src/ RUN ./mvnw package \ --define skipTests \ diff --git a/core/pom.xml b/core/pom.xml index e34cb44596..532b1d4d04 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -23,36 +23,14 @@ <dependency> <groupId>org.glassfish.jersey.core</groupId> <artifactId>jersey-client</artifactId> - <exclusions> - <exclusion> - <groupId>org.glassfish.hk2.external</groupId> - <artifactId>javax.inject</artifactId> - </exclusion> - </exclusions> </dependency> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-jaxb</artifactId> - <exclusions> - <exclusion> - <groupId>org.glassfish.hk2.external</groupId> - <artifactId>javax.inject</artifactId> - </exclusion> - </exclusions> </dependency> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> - <exclusions> - <exclusion> - <groupId>org.glassfish.hk2.external</groupId> - <artifactId>javax.inject</artifactId> - </exclusion> - </exclusions> - </dependency> - <dependency> - <groupId>org.glassfish.jersey.inject</groupId> - <artifactId>jersey-hk2</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> diff --git a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java index 267267e988..352c43456a 100644 --- a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java +++ b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java @@ -1033,11 +1033,6 @@ public class CoreConfig { ); } - @Bean - public DataInitializer dataInitializer() { - return new DataInitializer(); - } - @Bean public FinalSeminarActivityHandler finalSeminarActivityHandler( ActivityPlanFacade activityPlanFacade, diff --git a/core/src/main/java/se/su/dsv/scipro/match/ApplicationPeriod.java b/core/src/main/java/se/su/dsv/scipro/match/ApplicationPeriod.java index 9dc48661d9..b21a586607 100755 --- a/core/src/main/java/se/su/dsv/scipro/match/ApplicationPeriod.java +++ b/core/src/main/java/se/su/dsv/scipro/match/ApplicationPeriod.java @@ -176,18 +176,16 @@ public class ApplicationPeriod extends DomainObject { return Collections.unmodifiableSet(answerSet); } - public void setProjectTypes(Iterable<ProjectType> projectTypes) { - this.projectTypes.clear(); + public void setProjectTypes(Set<ProjectType> projectTypes) { + this.projectTypes.removeIf(appt -> !projectTypes.contains(appt.getProjectType())); for (ProjectType pt : projectTypes) { - this.projectTypes.add(new ApplicationPeriodProjectType(this, pt)); + if (this.projectTypes.stream().noneMatch(appt -> appt.getProjectType().equals(pt))) { + addProjectType(pt); + } } } public void addProjectType(ProjectType projectType) { this.projectTypes.add(new ApplicationPeriodProjectType(this, projectType)); } - - public void removeProjectType(ProjectType projectType) { - this.projectTypes.removeIf(next -> next.getProjectType().equals(projectType)); - } } diff --git a/pom.xml b/pom.xml index a43a6491cd..896b1269e5 100755 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ <module>daisy-integration</module> <module>war</module> <module>api</module> + <module>test-data</module> </modules> <properties> @@ -295,13 +296,6 @@ <artifactId>slf4j-api</artifactId> </dependency> - <!-- Additional dependencies --> - <dependency> - <groupId>javax.inject</groupId> - <artifactId>javax.inject</artifactId> - <version>1</version> - </dependency> - <!-- Test stuff --> <dependency> <groupId>org.junit.jupiter</groupId> diff --git a/test-data/pom.xml b/test-data/pom.xml new file mode 100644 index 0000000000..127cef4d71 --- /dev/null +++ b/test-data/pom.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>se.su.dsv.scipro</groupId> + <artifactId>SciPro</artifactId> + <version>0.1-SNAPSHOT</version> + </parent> + + <artifactId>test-data</artifactId> + + <dependencies> + <dependency> + <groupId>se.su.dsv.scipro</groupId> + <artifactId>core</artifactId> + </dependency> + </dependencies> +</project> diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/BaseData.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/BaseData.java new file mode 100644 index 0000000000..1dd6b18132 --- /dev/null +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/BaseData.java @@ -0,0 +1,19 @@ +package se.su.dsv.scipro.testdata; + +import se.su.dsv.scipro.system.ProjectType; + +/// All the base test data that can be re-used in different test cases. +/// +/// **Do not modify any of this data.** There are many +/// [TestDataPopulator]s that rely on this data to be in a specific state. +/// +/// In addition to the data that is available here there is also much additional +/// data that has been created; +/// +/// - A grading report template for each [ProjectType] +/// +public interface BaseData { + ProjectType bachelor(); + ProjectType magister(); + ProjectType master(); +} diff --git a/core/src/main/java/se/su/dsv/scipro/DataInitializer.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java similarity index 98% rename from core/src/main/java/se/su/dsv/scipro/DataInitializer.java rename to test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java index 466ec822ee..cf01ab2315 100644 --- a/core/src/main/java/se/su/dsv/scipro/DataInitializer.java +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java @@ -1,4 +1,4 @@ -package se.su.dsv.scipro; +package se.su.dsv.scipro.testdata; import jakarta.inject.Inject; import jakarta.inject.Provider; @@ -51,13 +51,16 @@ import se.su.dsv.scipro.security.auth.roles.Roles; import se.su.dsv.scipro.system.*; import se.su.dsv.scipro.util.Pair; -public class DataInitializer implements Lifecycle { +public class DataInitializer implements Lifecycle, BaseData, Factory { public static final int APPLICATION_PERIOD_START_MINUS_DAYS = 1; public static final int APPLICATION_PERIOD_END_PLUS_DAYS = 3; public static final int APPLICATION_PERIOD_COURSE_START_PLUS_DAYS = 5; public static final long RESEARCH_AREA_ID = 12L; + @Inject + private Optional<Collection<TestDataPopulator>> testDataPopulators = Optional.empty(); + @Inject private UserService userService; @@ -146,6 +149,10 @@ public class DataInitializer implements Lifecycle { createRoughDraftApproval(); createPastFinalSeminar(); setUpNotifications(); + Collection<TestDataPopulator> availablePopulators = testDataPopulators.orElseGet(Collections::emptySet); + for (TestDataPopulator testDataPopulator : availablePopulators) { + testDataPopulator.populate(this, this); + } } if (profile.getCurrentProfile() == Profiles.DEV && noAdminUser()) { createAdmin(); @@ -310,13 +317,18 @@ public class DataInitializer implements Lifecycle { sofia_student = createStudent("Sofia", 17); } - private User createStudent(String firstName, int identifier) { + private User createStudent(String firstName) { User user = createUser(firstName, STUDENT_LAST); - user.setIdentifier(identifier); createBeta(user); return user; } + private User createStudent(String firstName, int identifier) { + User user = createStudent(firstName); + user.setIdentifier(identifier); + return user; + } + private User createEmployee(String firstName) { User user = createUser(firstName, EMPLOYEE_LAST); Unit u = createUnit(); @@ -2151,6 +2163,36 @@ public class DataInitializer implements Lifecycle { return entity; } + @Override + public ProjectType bachelor() { + return bachelorClass; + } + + @Override + public ProjectType magister() { + return magisterClass; + } + + @Override + public ProjectType master() { + return masterClass; + } + + @Override + public User createAuthor(String firstName) { + return createStudent(firstName); + } + + @Override + public User createSupervisor(String firstName) { + return createEmployee(firstName); + } + + @Override + public User createReviewer(String firstName) { + return createEmployee(firstName); + } + private static final class SimpleTextFile implements FileUpload { private final User uploader; diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/Factory.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/Factory.java new file mode 100644 index 0000000000..fc4c0910f1 --- /dev/null +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/Factory.java @@ -0,0 +1,46 @@ +package se.su.dsv.scipro.testdata; + +import se.su.dsv.scipro.security.auth.roles.Roles; +import se.su.dsv.scipro.system.User; + +/** + * A factory to help with repetitive tasks when populating test data. + */ +public interface Factory { + /** + * Creates a user with the given first name and last name "Student". + * The user is given the role {@link Roles#AUTHOR}. + * <p> + * A username is created of the form {@code <first_name>@example.com} that + * can be used to log in. + */ + User createAuthor(String firstName); + + /** + * Creates a user with the given first name and last name "Employee". + * <p> + * The user is given the role {@link Roles#SUPERVISOR}, {@link Roles#REVIEWER}, + * and {@link Roles#EXAMINER}. + * <p> + * The user gets a default research area, unit, and language. It is also + * marked as {@link User#setActiveAsSupervisor(boolean) an active supervisor}. + * <p> + * A username is created of the form {@code <first_name>@example.com} that + * can be used to log in. + */ + User createSupervisor(String firstName); + + /** + * Creates a user with the given first name and last name "Employee". + * <p> + * The user is given the role {@link Roles#SUPERVISOR}, {@link Roles#REVIEWER}, + * and {@link Roles#EXAMINER}. + * <p> + * The user gets a default research area, unit, and language. It is also + * marked as {@link User#setActiveAsSupervisor(boolean) an active supervisor}. + * <p> + * A username is created of the form {@code <first_name>@example.com} that + * can be used to log in. + */ + User createReviewer(String firstName); +} diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/TestDataConfiguration.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/TestDataConfiguration.java new file mode 100644 index 0000000000..ffd372628b --- /dev/null +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/TestDataConfiguration.java @@ -0,0 +1,16 @@ +package se.su.dsv.scipro.testdata; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import se.su.dsv.scipro.war.PluginConfiguration; + +@Configuration(proxyBeanMethods = false) +@ComponentScan(basePackages = "se.su.dsv.scipro.testdata.populators") +public class TestDataConfiguration implements PluginConfiguration { + + @Bean + public DataInitializer dataInitializer() { + return new DataInitializer(); + } +} diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/TestDataPopulator.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/TestDataPopulator.java new file mode 100644 index 0000000000..41d250ac71 --- /dev/null +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/TestDataPopulator.java @@ -0,0 +1,11 @@ +package se.su.dsv.scipro.testdata; + +public interface TestDataPopulator { + /** + * Add test data to the system to help with testing a specific feature. + * + * @param baseData the base data already populated + * @param factory helper object to make repetitive tasks easier (such as creating users) + */ + void populate(BaseData baseData, Factory factory); +} diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/package-info.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/package-info.java new file mode 100644 index 0000000000..6b93353f29 --- /dev/null +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/package-info.java @@ -0,0 +1,8 @@ +/** + * This package contains the infrastructure that is used when generating test data for the application. To add new test + * data to the system, add a new class to the {@link se.su.dsv.scipro.testdata.populators} package that implements the + * {@link se.su.dsv.scipro.testdata.TestDataPopulator} interface and annotate it with + * {@link org.springframework.stereotype.Service @Service}. Inject dependencies as needed using + * {@link jakarta.inject.Inject @Inject}. + */ +package se.su.dsv.scipro.testdata; diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/package-info.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/package-info.java new file mode 100644 index 0000000000..c9d116ca5a --- /dev/null +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/package-info.java @@ -0,0 +1,13 @@ +/** + * Contains classes that populate the database with test data. + * <p> + * Prefer to use methods on the various services to create data, rather than directly interacting with the database + * using an {@link jakarta.persistence.EntityManager}. This is to make sure all business rules are enforced and that + * any additional logic is executed such as sending notifications or calculating statistics. + * + * @see se.su.dsv.scipro.testdata how to add new populators + * @see se.su.dsv.scipro.testdata.TestDataPopulator + * @see se.su.dsv.scipro.testdata.BaseData + * @see se.su.dsv.scipro.testdata.Factory + */ +package se.su.dsv.scipro.testdata.populators; diff --git a/test-data/src/main/resources/META-INF/services/se.su.dsv.scipro.war.PluginConfiguration b/test-data/src/main/resources/META-INF/services/se.su.dsv.scipro.war.PluginConfiguration new file mode 100644 index 0000000000..99b97f03e4 --- /dev/null +++ b/test-data/src/main/resources/META-INF/services/se.su.dsv.scipro.war.PluginConfiguration @@ -0,0 +1 @@ +se.su.dsv.scipro.testdata.TestDataConfiguration diff --git a/view/src/main/java/se/su/dsv/scipro/applicationperiod/AdminApplicationPeriodsPanel.java b/view/src/main/java/se/su/dsv/scipro/applicationperiod/AdminApplicationPeriodsPanel.java index 307d7858f3..b8bb7d9e76 100755 --- a/view/src/main/java/se/su/dsv/scipro/applicationperiod/AdminApplicationPeriodsPanel.java +++ b/view/src/main/java/se/su/dsv/scipro/applicationperiod/AdminApplicationPeriodsPanel.java @@ -123,13 +123,7 @@ public class AdminApplicationPeriodsPanel extends Panel { item.add( new DisplayMultiplesPanel<>( s, - new ListAdapterModel<>( - LambdaModel.of( - iModel, - ApplicationPeriod::getProjectTypes, - ApplicationPeriod::setProjectTypes - ) - ) + new ListAdapterModel<>(iModel.map(ApplicationPeriod::getProjectTypes)) ) { @Override public Component getComponent(String componentId, IModel<ProjectType> t) { diff --git a/view/src/main/java/se/su/dsv/scipro/datatables/target/AddTargetLinkPanel.java b/view/src/main/java/se/su/dsv/scipro/datatables/target/AddTargetLinkPanel.java index 2f4d027af5..8c4c98a6d0 100644 --- a/view/src/main/java/se/su/dsv/scipro/datatables/target/AddTargetLinkPanel.java +++ b/view/src/main/java/se/su/dsv/scipro/datatables/target/AddTargetLinkPanel.java @@ -22,12 +22,7 @@ public class AddTargetLinkPanel extends Panel { public AddTargetLinkPanel(String id, final IModel<ApplicationPeriod> model) { super(id, model); add( - new ListView<>( - "list", - new ListAdapterModel<>( - LambdaModel.of(model, ApplicationPeriod::getProjectTypes, ApplicationPeriod::setProjectTypes) - ) - ) { + new ListView<>("list", new ListAdapterModel<>(model.map(ApplicationPeriod::getProjectTypes))) { @Override protected void populateItem(ListItem<ProjectType> item) { item.add(new Label("pc", item.getModelObject().getName())); diff --git a/view/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerPage.java b/view/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerPage.java index a70588801a..ca3736e027 100755 --- a/view/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerPage.java +++ b/view/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerPage.java @@ -76,13 +76,7 @@ public class ProjectPartnerPage extends AbstractIdeaProjectPage implements MenuH } ); final IModel<? extends List<ProjectType>> matchableTypes = getMatchableTypes( - new ListAdapterModel<>( - LambdaModel.of( - applicationPeriod, - ApplicationPeriod::getProjectTypes, - ApplicationPeriod::setProjectTypes - ) - ) + new ListAdapterModel<>(applicationPeriod.map(ApplicationPeriod::getProjectTypes)) ); panelContainer.add( new ListView<>("ads", matchableTypes) { diff --git a/war/pom.xml b/war/pom.xml index c1dd889587..6a347c9b09 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -140,5 +140,18 @@ <spring.profile.active>branch</spring.profile.active> </properties> </profile> + <profile> + <id>DEV</id> + <activation> + <activeByDefault>true</activeByDefault> + </activation> + <dependencies> + <dependency> + <groupId>se.su.dsv.scipro</groupId> + <artifactId>test-data</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </profile> </profiles> </project> diff --git a/war/src/main/java/se/su/dsv/scipro/war/CurrentUserFromSpringSecurity.java b/war/src/main/java/se/su/dsv/scipro/war/CurrentUserFromSpringSecurity.java index 6f209f38aa..3d71fd12a3 100644 --- a/war/src/main/java/se/su/dsv/scipro/war/CurrentUserFromSpringSecurity.java +++ b/war/src/main/java/se/su/dsv/scipro/war/CurrentUserFromSpringSecurity.java @@ -4,6 +4,7 @@ import jakarta.inject.Inject; import jakarta.inject.Provider; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import java.io.Serializable; import java.security.Principal; import java.util.Collections; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -81,7 +82,7 @@ public class CurrentUserFromSpringSecurity implements AuthenticationContext { return authentication.getName(); } - private static final class WicketControlledPrincipal implements Principal { + private static final class WicketControlledPrincipal implements Principal, Serializable { private final String username;