This commit is contained in:
Wayne Westmoreland 2022-04-13 12:36:28 +02:00
parent 09b092ed56
commit b7c42aa13e
15 changed files with 211 additions and 122 deletions
core/src/main/java
daisy-integration/src
main/java/se/su/dsv/scipro
test/java/se/su/dsv/scipro/daisyExternal/impl
view/src
main/java/se/su/dsv/scipro
test/java/se/su/dsv/scipro

@ -149,6 +149,7 @@ public class CoreModule extends AbstractModule {
install(new MailModule());
install(new ForumModule());
Multibinder.newSetBinder(binder(), UserImportService.class);
Multibinder.newSetBinder(binder(), UserSearchService.class);
}
}

@ -6,8 +6,6 @@ import io.opentracing.Tracer;
import io.opentracing.noop.NoopTracerFactory;
import se.su.dsv.scipro.daisyExternal.http.DaisyAPI;
import se.su.dsv.scipro.daisyExternal.http.DaisyAPIImpl;
import se.su.dsv.scipro.daisyExternal.impl.ExternalImporterDaisyImpl;
import se.su.dsv.scipro.daisyExternal.impl.ImporterTransactionsImpl;
public class DaisyExternalModule extends PrivateModule {
@Override
@ -16,12 +14,7 @@ public class DaisyExternalModule extends PrivateModule {
requireBinding(Key.get(String.class, Names.named("daisy.api.username")));
requireBinding(Key.get(String.class, Names.named("daisy.api.password")));
bind(ExternalImporter.class).to(ExternalImporterDaisyImpl.class);
bind(ImporterTransactions.class).to(ImporterTransactionsImpl.class);
bind(DaisyAPI.class).to(DaisyAPIImpl.class).in(Scopes.SINGLETON);
expose(ExternalImporter.class);
expose(ImporterTransactions.class);
expose(DaisyAPI.class);
expose(Tracer.class);
}

@ -1,22 +0,0 @@
package se.su.dsv.scipro.daisyExternal;
import se.su.dsv.scipro.system.ResearchArea;
import se.su.dsv.scipro.system.User;
import java.util.Optional;
import java.util.Set;
/**
* Specifies interaction of an importing service component.
*/
public interface ExternalImporter {
Optional<User> importUser(final String userName);
void importSupervisorUnits();
void importSupervisorActiveStatus();
Set<ResearchArea> importResearchAreasForSupervisor(User supervisor);
}

@ -0,0 +1,46 @@
package se.su.dsv.scipro.system;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public interface UserImportService {
Optional<User> importUser(final String userName);
List<ImportableUser> search(String searchTerm);
void importUser(ImportableUser importableUser);
Set<ResearchArea> importResearchAreasForSupervisor(User supervisor);
class ImportableUser implements Serializable {
private final String firstName;
private final String lastName;
private final String email;
private final Integer id;
public ImportableUser(String firstName, String lastName, String email, Integer id) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.id = id;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getEmail() {
return email;
}
public Integer getId() {
return id;
}
}
}

@ -0,0 +1,12 @@
package se.su.dsv.scipro.daisyExternal;
/**
* Specifies interaction of an importing service component.
*/
public interface ExternalImporter {
void importSupervisorUnits();
void importSupervisorActiveStatus();
}

@ -16,16 +16,18 @@ import se.su.dsv.scipro.security.auth.roles.Roles;
import se.su.dsv.scipro.springdata.services.UnitService;
import se.su.dsv.scipro.system.ResearchArea;
import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.system.UserImportService;
import se.su.dsv.scipro.system.UserService;
import javax.inject.Inject;
import javax.ws.rs.ClientErrorException;
import java.util.*;
import java.util.stream.Collectors;
/**
* Importer implementation, uses standard HTTP requests and Json-wrappers.
*/
public class ExternalImporterDaisyImpl implements ExternalImporter {
public class ExternalImporterDaisyImpl implements ExternalImporter, UserImportService {
public static final Integer DSV = 4;
private static final Logger LOGGER = LoggerFactory.getLogger(ExternalImporterDaisyImpl.class);
@ -47,6 +49,33 @@ public class ExternalImporterDaisyImpl implements ExternalImporter {
this.importerTransactions = importerTransactions;
}
@Override
public List<ImportableUser> search(String searchTerm) {
return client.findByPersonnummer(searchTerm)
.stream()
.filter(person -> !person.isDeceased())
.map(this::toImportableUser)
.collect(Collectors.toList());
}
private ImportableUser toImportableUser(Person person) {
return new DaisyImportableUser(person);
}
private static class DaisyImportableUser extends ImportableUser {
public DaisyImportableUser(Person person) {
super(person.getFirstName(), person.getLastName(), person.getEmail(), person.getId());
}
}
@Override
public void importUser(ImportableUser importableUser) {
if (importableUser instanceof DaisyImportableUser) {
client.findPersonById(importableUser.getId())
.ifPresent(this::importPerson);
}
}
@Override
public Optional<User> importUser(final String userName) {
try {

@ -2,16 +2,24 @@ package se.su.dsv.scipro.integration.daisy;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.servlet.ServletModule;
import se.su.dsv.scipro.daisyExternal.ExternalImporter;
import se.su.dsv.scipro.daisyExternal.ImporterTransactions;
import se.su.dsv.scipro.daisyExternal.impl.ExternalImporterDaisyImpl;
import se.su.dsv.scipro.daisyExternal.impl.ImporterTransactionsImpl;
import se.su.dsv.scipro.integration.daisy.workers.ProjectExporter;
import se.su.dsv.scipro.integration.daisy.workers.UserImportWorker;
import se.su.dsv.scipro.io.ExternalExporter;
import se.su.dsv.scipro.io.impl.ExternalExporterDaisyImpl;
import se.su.dsv.scipro.match.IdeaCreationJudge;
import se.su.dsv.scipro.system.UserImportService;
import se.su.dsv.scipro.system.UserSearchService;
public class DaisyModule extends ServletModule {
@Override
protected void configureServlets() {
bind(ExternalImporter.class).to(ExternalImporterDaisyImpl.class);
bind(ImporterTransactions.class).to(ImporterTransactionsImpl.class);
Multibinder<IdeaCreationJudge> judges = Multibinder.newSetBinder(binder(), IdeaCreationJudge.class);
judges.addBinding().to(Daisy.class);
@ -20,6 +28,9 @@ public class DaisyModule extends ServletModule {
bind(ProjectExporter.class);
bind(DaisyWorkerInitialization.class).asEagerSingleton();
Multibinder.newSetBinder(binder(), UserImportService.class)
.addBinding().to(ExternalImporterDaisyImpl.class);
Multibinder.newSetBinder(binder(), UserSearchService.class)
.addBinding().to(DaisyUserSearchService.class);
}

@ -0,0 +1,50 @@
package se.su.dsv.scipro.daisyExternal.impl;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import se.su.dsv.scipro.daisyExternal.ImporterTransactions;
import se.su.dsv.scipro.daisyExternal.http.DaisyAPI;
import se.su.dsv.scipro.io.dto.Person;
import se.su.dsv.scipro.springdata.services.UnitService;
import se.su.dsv.scipro.system.UserImportService;
import se.su.dsv.scipro.system.UserService;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class ExternalImporterDaisyImplTest {
@Mock
private DaisyAPI daisyAPI;
@Mock
private UserService userService;
@Mock
private UnitService unitService;
@Mock
private ImporterTransactions importerTransactions;
@Test
void search_excludes_deceased_people() {
Person einstein = new Person();
einstein.setLastName("Einstein");
einstein.setDeceased(true);
Person obama = new Person();
obama.setLastName("Obama");
obama.setDeceased(false);
when(daisyAPI.findByPersonnummer(anyString())).thenReturn(List.of(einstein, obama));
ExternalImporterDaisyImpl importerDaisy = new ExternalImporterDaisyImpl(userService, unitService, daisyAPI, importerTransactions);
List<UserImportService.ImportableUser> importableUsers = importerDaisy.search("abc");
assertEquals(1, importableUsers.size());
assertEquals("Obama", importableUsers.get(0).getLastName());
}
}

@ -1,21 +1,22 @@
package se.su.dsv.scipro.loginlogout.pages;
import se.su.dsv.scipro.basepages.PublicPage;
import se.su.dsv.scipro.daisyExternal.ExternalImporter;
import se.su.dsv.scipro.security.auth.Authorization;
import se.su.dsv.scipro.session.SciProSession;
import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.system.UserImportService;
import se.su.dsv.scipro.system.UserService;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import java.util.Optional;
import java.util.Set;
@Authorization(requiresLoggedInUser = false)
public class SSOPage extends PublicPage {
@Inject
private ExternalImporter externalImporter;
private Set<UserImportService> userImportServices;
@Inject
private UserService userService;
@ -29,12 +30,14 @@ public class SSOPage extends PublicPage {
continueToOriginalDestination();
setResponsePage(getApplication().getHomePage());
} else {
Optional<User> optionalUser = externalImporter.importUser(remoteUserName);
if (optionalUser.isPresent()) {
SciProSession.get().setUser(optionalUser.get());
continueToOriginalDestination();
setResponsePage(getApplication().getHomePage());
for (UserImportService userImportService : userImportServices) {
Optional<User> optionalUser = userImportService.importUser(remoteUserName);
if (optionalUser.isPresent()) {
SciProSession.get().setUser(optionalUser.get());
continueToOriginalDestination();
setResponsePage(getApplication().getHomePage());
break;
}
}
}
}

@ -7,11 +7,11 @@ import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import se.su.dsv.scipro.daisyExternal.ExternalImporter;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettings;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
import se.su.dsv.scipro.system.ResearchArea;
import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.system.UserImportService;
import javax.inject.Inject;
import java.util.Set;
@ -21,7 +21,7 @@ public class UpdateResearchAreasPanel extends Panel {
@Inject
GeneralSystemSettingsService generalSystemSettingsService;
@Inject
ExternalImporter externalImporter;
Set<UserImportService> userImportServices;
public UpdateResearchAreasPanel(final String id, final IModel<User> supervisor) {
super(id, supervisor);
@ -36,7 +36,10 @@ public class UpdateResearchAreasPanel extends Panel {
add(new AjaxLink<>("sync", supervisor) {
@Override
public void onClick(AjaxRequestTarget target) {
Set<ResearchArea> imported = externalImporter.importResearchAreasForSupervisor(getModelObject());
Set<ResearchArea> imported = userImportServices.stream()
.map(uis -> uis.importResearchAreasForSupervisor(getModelObject()))
.flatMap(Set::stream)
.collect(Collectors.toSet());
if (imported.stream().allMatch(ResearchArea::isDeleted)) {
error(getString("no.research.areas.found"));
}

@ -10,27 +10,25 @@ import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.ResourceModel;
import se.su.dsv.scipro.daisyExternal.ImporterTransactions;
import se.su.dsv.scipro.daisyExternal.http.DaisyAPI;
import se.su.dsv.scipro.io.dto.Person;
import se.su.dsv.scipro.system.UserImportService;
import se.su.dsv.scipro.system.UserImportService.ImportableUser;
import se.su.dsv.scipro.system.UserService;
import javax.inject.Inject;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class AdminImportUserPage extends AbstractAdminUsersPage {
@Inject
private DaisyAPI daisyAPI;
private Set<UserImportService> userImportServices;
@Inject
private UserService userService;
@Inject
private ImporterTransactions importerTransactions;
private final IModel<String> searchTermModel = Model.of("");
private final IModel<List<Person>> daisyPeopleModel = Model.ofList(Collections.emptyList());
private final IModel<List<ImportableUser>> importablePeopleModel = Model.ofList(Collections.emptyList());
public AdminImportUserPage() {
add(new FeedbackPanel("feedback"));
@ -42,32 +40,32 @@ public class AdminImportUserPage extends AbstractAdminUsersPage {
@Override
protected void onSubmit() {
final List<Person> byPersonnummer = daisyAPI.findByPersonnummer(searchTermModel.getObject());
daisyPeopleModel.setObject(byPersonnummer);
List<ImportableUser> byPersonnummer = userImportServices.stream()
.map(uis -> uis.search(searchTermModel.getObject()))
.flatMap(List::stream)
.collect(Collectors.toList());
importablePeopleModel.setObject(byPersonnummer);
}
});
add(new ListView<>("importCandidates", daisyPeopleModel) {
add(new ListView<>("importCandidates", importablePeopleModel) {
@Override
protected void populateItem(final ListItem<Person> item) {
final IModel<Person> person = item.getModel();
item.add(new Label("firstName", person.map(Person::getFirstName)));
item.add(new Label("lastName", person.map(Person::getLastName)));
item.add(new Label("email", person.map(Person::getEmail)));
protected void populateItem(final ListItem<ImportableUser> item) {
final IModel<ImportableUser> importableUser = item.getModel();
item.add(new Label("firstName", importableUser.map(ImportableUser::getFirstName)));
item.add(new Label("lastName", importableUser.map(ImportableUser::getLastName)));
item.add(new Label("email", importableUser.map(ImportableUser::getEmail)));
item.add(new Link<>("import", item.getModel()) {
@Override
protected void onConfigure() {
super.onConfigure();
final Person person = getModelObject();
final boolean alreadyImported = userService.findByExternalIdentifier(person.getId()) != null;
final ImportableUser importableUser1 = getModelObject();
final boolean alreadyImported = userService.findByExternalIdentifier(importableUser1.getId()) != null;
if (alreadyImported) {
setEnabled(false);
setBody(new ResourceModel("person.already.imported", "Already imported"));
} else if (person.isDeceased()) {
setEnabled(false);
setBody(new ResourceModel("person.is.deceased", "The person is deceased"));
} else if (person.getEmail() == null) {
} else if (importableUser1.getEmail() == null) {
setEnabled(false);
setBody(new ResourceModel("person.lacks.email", "Must specify an e-mail address in Daisy"));
} else {
@ -78,7 +76,10 @@ public class AdminImportUserPage extends AbstractAdminUsersPage {
@Override
public void onClick() {
importerTransactions.importStudent(getModelObject(), new HashMap<>());
ImportableUser toImport = getModelObject();
for (UserImportService userImportService : userImportServices) {
userImportService.importUser(toImport);
}
getPage().success(getString("person.imported", getModel()));
}
});

@ -20,7 +20,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
import se.su.dsv.scipro.activityplan.*;
import se.su.dsv.scipro.admin.pages.AdminStartPage;
import se.su.dsv.scipro.checklist.*;
import se.su.dsv.scipro.daisyExternal.ImporterTransactions;
import se.su.dsv.scipro.daisyExternal.http.DaisyAPI;
import se.su.dsv.scipro.daisyExternal.http.PhotoResult;
import se.su.dsv.scipro.data.enums.DateStyle;
@ -294,7 +293,7 @@ public abstract class SciProTest {
@Mock
protected GradeCalculatorService gradeCalculatorService;
@Mock
protected ImporterTransactions importerTransactions;
protected UserImportService userImportService;
@Mock
protected Reporter reporter;
@ -411,6 +410,8 @@ public abstract class SciProTest {
@Override
protected void configure() {
Multibinder.newSetBinder(binder(), Lifecycle.class);
Multibinder.newSetBinder(binder(), UserImportService.class)
.addBinding().toInstance(userImportService);
bindConstant().annotatedWith(Names.named("profile")).to("TEST");
for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
for (Field f : c.getDeclaredFields()) {

@ -3,10 +3,9 @@ package se.su.dsv.scipro.user;
import org.apache.wicket.util.tester.FormTester;
import org.junit.jupiter.api.Test;
import se.su.dsv.scipro.SciProTest;
import se.su.dsv.scipro.io.dto.Person;
import se.su.dsv.scipro.system.UserImportService;
import java.util.Collections;
import java.util.HashMap;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -15,38 +14,21 @@ public class AdminImportUserPageTest extends SciProTest {
@Test
public void search() {
final String searchTerm = "199506";
final String firstName = "John";
final String lastName = "Doe";
final Person person = new Person();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setEmail("john@example.com");
person.setDeceased(false);
when(daisyAPI.findByPersonnummer(searchTerm)).thenReturn(Collections.singletonList(person));
UserImportService.ImportableUser importableUser = fakeImportableUser(searchTerm, "john@example.com");
tester.startPage(AdminImportUserPage.class);
final FormTester formTester = tester.newFormTester("form");
formTester.setValue("searchTerm", searchTerm);
formTester.submit();
tester.assertLabel(path("importCandidates", "0", "firstName"), firstName);
tester.assertLabel(path("importCandidates", "0", "firstName"), importableUser.getFirstName());
}
@Test
public void cant_imported_people_without_email() {
final String searchTerm = "199506";
final String firstName = "John";
final String lastName = "Doe";
final Person person = new Person();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setEmail(null);
person.setDeceased(false);
when(daisyAPI.findByPersonnummer(searchTerm)).thenReturn(Collections.singletonList(person));
fakeImportableUser(searchTerm, null);
tester.startPage(AdminImportUserPage.class);
final FormTester formTester = tester.newFormTester("form");
@ -56,42 +38,10 @@ public class AdminImportUserPageTest extends SciProTest {
tester.assertDisabled(path("importCandidates", "0", "import"));
}
@Test
public void cant_imported_deceased_people() {
final String searchTerm = "199506";
final String firstName = "John";
final String lastName = "Doe";
final Person person = new Person();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setEmail("john@example.com");
person.setDeceased(true);
when(daisyAPI.findByPersonnummer(searchTerm)).thenReturn(Collections.singletonList(person));
tester.startPage(AdminImportUserPage.class);
final FormTester formTester = tester.newFormTester("form");
formTester.setValue("searchTerm", searchTerm);
formTester.submit();
tester.assertDisabled(path("importCandidates", "0", "import"));
}
@Test
public void import_person() {
final String searchTerm = "199506";
final String firstName = "John";
final String lastName = "Doe";
final Person person = new Person();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setEmail("john@example.com");
person.setDeceased(false);
when(daisyAPI.findByPersonnummer(searchTerm)).thenReturn(Collections.singletonList(person));
UserImportService.ImportableUser importableUser = fakeImportableUser(searchTerm, "john@example.com");
tester.startPage(AdminImportUserPage.class);
final FormTester formTester = tester.newFormTester("form");
@ -100,6 +50,17 @@ public class AdminImportUserPageTest extends SciProTest {
tester.clickLink(path("importCandidates", "0", "import"));
verify(importerTransactions).importStudent(person, new HashMap<>());
verify(userImportService).importUser(importableUser);
}
private UserImportService.ImportableUser fakeImportableUser(String searchTerm, String email) {
final String firstName = "John";
final String lastName = "Doe";
UserImportService.ImportableUser importableUser =
new UserImportService.ImportableUser(firstName, lastName, email, 42);
when(userImportService.search(searchTerm)).thenReturn(Collections.singletonList(importableUser));
return importableUser;
}
}