diff --git a/citesphere/pom.xml b/citesphere/pom.xml index 871f987ae..6a7cabf43 100644 --- a/citesphere/pom.xml +++ b/citesphere/pom.xml @@ -24,8 +24,10 @@ 6.2.3 2.2.6.RELEASE 0.13 - 0.4 - 1.24 + + 0.5 + 1.25 + 0.2 $2a$04$oQo44vqcDIFRoYKiAXoNheurzkwX9dcNmowvTX/hsWuBMwijqn44i @@ -162,6 +164,11 @@ citesphere-model ${citesphere.model.version} + + edu.asu.diging + crossref-connect + ${crossref-connect-version} + @@ -337,7 +344,7 @@ - + javax.annotation javax.annotation-api 1.3.2 diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/api/v1/user/JobInfoController.java b/citesphere/src/main/java/edu/asu/diging/citesphere/api/v1/user/JobInfoController.java index cab48f00c..2e6eabf2f 100644 --- a/citesphere/src/main/java/edu/asu/diging/citesphere/api/v1/user/JobInfoController.java +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/api/v1/user/JobInfoController.java @@ -1,5 +1,11 @@ package edu.asu.diging.citesphere.api.v1.user; +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -8,15 +14,21 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import edu.asu.diging.citesphere.core.export.IExportTaskManager; import edu.asu.diging.citesphere.core.model.IZoteroToken; import edu.asu.diging.citesphere.core.model.export.IExportTask; import edu.asu.diging.citesphere.core.model.jobs.IExportJob; +import edu.asu.diging.citesphere.core.model.jobs.IImportCrossrefJob; import edu.asu.diging.citesphere.core.model.jobs.IJob; import edu.asu.diging.citesphere.core.model.jobs.IUploadJob; +import edu.asu.diging.citesphere.core.model.jobs.impl.ExportJob; +import edu.asu.diging.citesphere.core.model.jobs.impl.ImportCrossrefJob; +import edu.asu.diging.citesphere.core.model.jobs.impl.UploadJob; import edu.asu.diging.citesphere.core.service.jobs.IUploadJobManager; import edu.asu.diging.citesphere.core.service.jwt.IJobApiTokenContents; import edu.asu.diging.citesphere.core.user.IUserManager; @@ -25,6 +37,8 @@ @Controller public class JobInfoController extends BaseJobInfoController { + private final Logger logger = LoggerFactory.getLogger(getClass()); + @Autowired private IUserManager userManager; @@ -35,9 +49,17 @@ public class JobInfoController extends BaseJobInfoController { private IUploadJobManager jobManager; @Autowired - private IExportTaskManager exportTaskManager; + private IExportTaskManager exportTaskManager; + private Map, BiConsumer> jobHandlers = new HashMap<>(); + public JobInfoController() { + // Map each job class to its respective handling method + jobHandlers.put(UploadJob.class, (node, job) -> handleUploadJob(node, (IUploadJob) job)); + jobHandlers.put(ExportJob.class, (node, job) -> handleExportJob(node, (IExportJob) job)); + jobHandlers.put(ImportCrossrefJob.class, (node, job) -> handleImportCrossrefJob(node, (IImportCrossrefJob) job)); + } + @RequestMapping(value="/job/info") public ResponseEntity getProfile(@RequestHeader HttpHeaders headers) { ResponseEntity entity = checkForToken(headers); @@ -53,25 +75,39 @@ public ResponseEntity getProfile(@RequestHeader HttpHeaders headers) { IJob job = jobManager.findJob(tokenContents.getJobId()); IZoteroToken zoteroToken = tokenManager.getToken(userManager.findByUsername(job.getUsername())); - + ObjectMapper mapper = new ObjectMapper(); ObjectNode node = mapper.createObjectNode(); node.put("zotero", zoteroToken.getToken()); node.put("zoteroId", zoteroToken.getUserId()); node.put("username", job.getUsername()); - // FIXME: ugly, needs better solution - if (job instanceof IUploadJob) { - node.put("groupId", ((IUploadJob)job).getCitationGroup()); - } - if (job instanceof IExportJob) { - IExportTask exportTask = exportTaskManager.get(((IExportJob)job).getTaskId()); - node.put("groupId", exportTask.getGroupId()); - node.put("collectionId", exportTask.getCollectionId()); - node.put("exportType", exportTask.getExportType().name()); - node.put("taskId", exportTask.getId()); + BiConsumer handler = jobHandlers.get(job.getClass()); + if (handler != null) { + handler.accept(node, job); + } else { + logger.error("No handler found for job type: " + job.getClass()); + return new ResponseEntity<>("No handler found for job type: " + job.getClass(), HttpStatus.INTERNAL_SERVER_ERROR); } - - + return new ResponseEntity<>(node.toString(), HttpStatus.OK); } + + private void handleUploadJob(ObjectNode node, IUploadJob job) { + node.put("groupId", job.getCitationGroup()); + } + + private void handleExportJob(ObjectNode node, IExportJob job) { + IExportTask exportTask = exportTaskManager.get(job.getTaskId()); + node.put("groupId", exportTask.getGroupId()); + node.put("collectionId", exportTask.getCollectionId()); + node.put("exportType", exportTask.getExportType().name()); + node.put("taskId", exportTask.getId()); + } + + private void handleImportCrossrefJob(ObjectNode node, IImportCrossrefJob job) { + ObjectMapper mapper = new ObjectMapper(); + ArrayNode arrayNode = mapper.valueToTree(job.getDois()); + node.put("dois", arrayNode); + node.put("groupId", job.getCitationGroup()); + } } diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/IImportCrossrefJob.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/IImportCrossrefJob.java new file mode 100644 index 000000000..c83bb233d --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/IImportCrossrefJob.java @@ -0,0 +1,26 @@ +package edu.asu.diging.citesphere.core.model.jobs; + +import java.util.List; + +import edu.asu.diging.citesphere.model.bib.ICitationGroup; + +public interface IImportCrossrefJob extends IJob { + + /** + * Get the DOIs of the resources to be imported from Crossref. + * @return list of resources to be imported + */ + List getDois(); + + void setDois(List dois); + + void setCitationGroup(String citationGroup); + + String getCitationGroup(); + + ICitationGroup getCitationGroupDetail(); + + void setCitationGroupDetail(ICitationGroup citationGroupDetail); + + +} \ No newline at end of file diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/impl/ImportCrossrefJob.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/impl/ImportCrossrefJob.java new file mode 100644 index 000000000..1527e63ce --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/impl/ImportCrossrefJob.java @@ -0,0 +1,58 @@ +package edu.asu.diging.citesphere.core.model.jobs.impl; + +import java.util.List; + +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.Transient; + +import edu.asu.diging.citesphere.core.model.jobs.IImportCrossrefJob; +import edu.asu.diging.citesphere.model.bib.ICitationGroup; + +@Entity +public class ImportCrossrefJob extends Job implements IImportCrossrefJob { + + @ElementCollection + private List dois; + private String citationGroup; + @Transient + private ICitationGroup citationGroupDetail; + + /** + * Get the DOIs of the resources to be imported from Crossref. + * @return list of resources to be imported + */ + @Override + public List getDois() { + return dois; + } + + @Override + public void setDois(List dois) { + this.dois = dois; + } + + @Override + public String getCitationGroup() { + return citationGroup; + } + + @Override + public void setCitationGroup(String citationGroup) { + this.citationGroup = citationGroup; + } + + @Override + public ICitationGroup getCitationGroupDetail() { + return citationGroupDetail; + } + + @Override + public void setCitationGroupDetail(ICitationGroup citationGroupDetail) { + this.citationGroupDetail = citationGroupDetail; + } + + + + +} diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/impl/Job.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/impl/Job.java index e05e332e7..52c643d90 100644 --- a/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/impl/Job.java +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/model/jobs/impl/Job.java @@ -1,6 +1,7 @@ package edu.asu.diging.citesphere.core.model.jobs.impl; import java.time.OffsetDateTime; +import java.util.ArrayList; import java.util.List; import javax.persistence.CascadeType; @@ -42,7 +43,7 @@ public class Job implements IJob { @JsonIgnore @OneToMany(cascade=CascadeType.ALL) - private List phases; + private List phases = new ArrayList<>(); /* (non-Javadoc) diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/repository/jobs/ImportCrossrefJobRepository.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/repository/jobs/ImportCrossrefJobRepository.java new file mode 100644 index 000000000..d61f20a27 --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/repository/jobs/ImportCrossrefJobRepository.java @@ -0,0 +1,9 @@ +package edu.asu.diging.citesphere.core.repository.jobs; + +import org.springframework.data.repository.CrudRepository; + +import edu.asu.diging.citesphere.core.model.jobs.impl.ImportCrossrefJob; + +public interface ImportCrossrefJobRepository extends CrudRepository { + +} diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/CrossrefService.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/CrossrefService.java new file mode 100644 index 000000000..f331608fa --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/CrossrefService.java @@ -0,0 +1,13 @@ +package edu.asu.diging.citesphere.core.service.crossref; + +import java.io.IOException; +import java.util.List; + +import edu.asu.diging.crossref.exception.RequestFailedException; +import edu.asu.diging.crossref.model.Item; + +public interface CrossrefService { + + List search(String query, int page) throws RequestFailedException, IOException; + +} \ No newline at end of file diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/impl/CrossrefServiceImpl.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/impl/CrossrefServiceImpl.java new file mode 100644 index 000000000..fbcb80de3 --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/crossref/impl/CrossrefServiceImpl.java @@ -0,0 +1,38 @@ +package edu.asu.diging.citesphere.core.service.crossref.impl; + +import java.io.IOException; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Service; + +import edu.asu.diging.citesphere.core.service.crossref.CrossrefService; +import edu.asu.diging.crossref.exception.RequestFailedException; +import edu.asu.diging.crossref.model.Item; +import edu.asu.diging.crossref.service.CrossrefConfiguration; +import edu.asu.diging.crossref.service.CrossrefWorksService; +import edu.asu.diging.crossref.service.impl.CrossrefWorksServiceImpl; + +@Service +@PropertySource("classpath:config.properties") +public class CrossrefServiceImpl implements CrossrefService { + + @Value("${_crossref_default_pagesize}") + private int defaultPageSize; + + private CrossrefWorksService service; + + @PostConstruct + public void init() { + service = new CrossrefWorksServiceImpl(CrossrefConfiguration.getDefaultConfig()); + } + + + @Override + public List search(String query, int page) throws RequestFailedException, IOException { + return service.search(query, defaultPageSize, (page-1)*defaultPageSize); + } +} diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/jobs/IImportCrossrefJobManager.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/jobs/IImportCrossrefJobManager.java new file mode 100644 index 000000000..4623d3821 --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/jobs/IImportCrossrefJobManager.java @@ -0,0 +1,13 @@ +package edu.asu.diging.citesphere.core.service.jobs; + +import java.util.List; + +import edu.asu.diging.citesphere.core.exceptions.GroupDoesNotExistException; +import edu.asu.diging.citesphere.core.model.jobs.IImportCrossrefJob; +import edu.asu.diging.citesphere.user.IUser; + +public interface IImportCrossrefJobManager { + + IImportCrossrefJob createJob(IUser user, String groupId, List dois) throws GroupDoesNotExistException; + +} \ No newline at end of file diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/jobs/impl/ImportCrossrefJobManager.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/jobs/impl/ImportCrossrefJobManager.java new file mode 100644 index 000000000..39f340f57 --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/jobs/impl/ImportCrossrefJobManager.java @@ -0,0 +1,80 @@ +package edu.asu.diging.citesphere.core.service.jobs.impl; + +import java.time.OffsetDateTime; +import java.util.List; + +import javax.transaction.Transactional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Service; + +import edu.asu.diging.citesphere.core.exceptions.GroupDoesNotExistException; +import edu.asu.diging.citesphere.core.exceptions.MessageCreationException; +import edu.asu.diging.citesphere.core.kafka.IKafkaRequestProducer; +import edu.asu.diging.citesphere.core.model.jobs.IImportCrossrefJob; +import edu.asu.diging.citesphere.core.model.jobs.JobStatus; +import edu.asu.diging.citesphere.core.model.jobs.impl.ImportCrossrefJob; +import edu.asu.diging.citesphere.core.model.jobs.impl.JobPhase; +import edu.asu.diging.citesphere.core.repository.jobs.ImportCrossrefJobRepository; +import edu.asu.diging.citesphere.core.service.IGroupManager; +import edu.asu.diging.citesphere.core.service.jobs.IImportCrossrefJobManager; +import edu.asu.diging.citesphere.core.service.jwt.IJwtTokenService; +import edu.asu.diging.citesphere.messages.KafkaTopics; +import edu.asu.diging.citesphere.messages.model.KafkaJobMessage; +import edu.asu.diging.citesphere.model.bib.ICitationGroup; +import edu.asu.diging.citesphere.user.IUser; + +@Service +@Transactional +@PropertySource("classpath:/config.properties") +public class ImportCrossrefJobManager implements IImportCrossrefJobManager { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Value("${_job_page_size}") + private int jobPageSize; + + @Autowired + private IGroupManager groupManager; + + @Autowired + private ImportCrossrefJobRepository jobRepo; + + @Autowired + private IKafkaRequestProducer kafkaProducer; + + @Autowired + private IJwtTokenService tokenService; + + @Override + public IImportCrossrefJob createJob(IUser user, String groupId, List dois) throws GroupDoesNotExistException { + ICitationGroup group = groupManager.getGroup(user, groupId); + if (group == null) { + throw new GroupDoesNotExistException("Group does not exists"); + } + + ImportCrossrefJob job = new ImportCrossrefJob(); + job.setCreatedOn(OffsetDateTime.now()); + job.setUsername(user.getUsername()); + job.setDois(dois); + job.setCitationGroup(groupId); + job.setStatus(JobStatus.PREPARED); + jobRepo.save(job); + + String token = tokenService.generateJobApiToken(job); + try { + kafkaProducer.sendRequest(new KafkaJobMessage(token), KafkaTopics.REFERENCES_IMPORT_CROSSREF_TOPIC); + } catch (MessageCreationException e) { + logger.error("Could not send Kafka message.", e); + job.setStatus(JobStatus.FAILURE); + job.getPhases().add(new JobPhase(JobStatus.FAILURE, e.getMessage())); + jobRepo.save(job); + } + + return job; + } +} diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/jobs/ImportCrossrefController.java b/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/jobs/ImportCrossrefController.java new file mode 100644 index 000000000..0ac305282 --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/jobs/ImportCrossrefController.java @@ -0,0 +1,84 @@ +package edu.asu.diging.citesphere.web.user.jobs; + +import java.io.IOException; +import java.security.Principal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import edu.asu.diging.citesphere.core.exceptions.GroupDoesNotExistException; +import edu.asu.diging.citesphere.core.service.ICitationManager; +import edu.asu.diging.citesphere.core.service.crossref.CrossrefService; +import edu.asu.diging.citesphere.core.service.jobs.IImportCrossrefJobManager; +import edu.asu.diging.citesphere.core.user.IUserManager; +import edu.asu.diging.citesphere.user.IUser; +import edu.asu.diging.crossref.exception.RequestFailedException; +import edu.asu.diging.crossref.model.Item; + +@Controller +public class ImportCrossrefController { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private CrossrefService crossrefService; + + @Autowired + private ICitationManager citationManager; + + @Autowired + private IUserManager userManager; + + @Autowired + private IImportCrossrefJobManager jobManager; + + @RequestMapping("/auth/import/crossref") + public String get(Model model, Principal principal) { + model.addAttribute("groups", citationManager.getGroups(userManager.findByUsername(principal.getName()))); + return "auth/import/crossref"; + } + + @RequestMapping("/auth/import/crossref/search") + public ResponseEntity> search(@RequestParam("query") String query, @RequestParam("page") int page) { + List results = null; + try { + results = crossrefService.search(query, page); + } catch (RequestFailedException | IOException e) { + logger.error("Could not get Crossref results.", e); + return new ResponseEntity>(HttpStatus.INTERNAL_SERVER_ERROR); + } + + return new ResponseEntity>(results, HttpStatus.OK); + } + + @RequestMapping(value="/auth/import/crossref", method=RequestMethod.POST) + public ResponseEntity> post(Authentication authentication, @RequestParam("groupId") String groupId, + @RequestParam("dois[]") List dois, Model model) { + Map response = new HashMap<>(); + try { + jobManager.createJob((IUser) authentication.getPrincipal(), groupId, dois); + response.put("show_alert", true); + response.put("alert_type", "success"); + response.put("alert_msg", "Import in progress."); + } catch (GroupDoesNotExistException e) { + logger.error("Could not create crossref job because group does not exist.", e); + response.put("show_alert", true); + response.put("alert_type", "danger"); + response.put("alert_msg", e.getMessage()); + return new ResponseEntity>(response, HttpStatus.OK); + } + return new ResponseEntity>(response, HttpStatus.OK); + } +} diff --git a/citesphere/src/main/resources/config.properties b/citesphere/src/main/resources/config.properties index dfd3eb1e8..18b5f332d 100644 --- a/citesphere/src/main/resources/config.properties +++ b/citesphere/src/main/resources/config.properties @@ -96,3 +96,5 @@ giles_check_endpoint=/api/v2/files/upload/check/ giles_file_endpoint=/api/v2/resources/files/{0}/content javers_default_author=${javers.default.author} + +_crossref_default_pagesize=20 diff --git a/citesphere/src/main/webapp/WEB-INF/views/auth/import/crossref.html b/citesphere/src/main/webapp/WEB-INF/views/auth/import/crossref.html new file mode 100644 index 000000000..fa7680731 --- /dev/null +++ b/citesphere/src/main/webapp/WEB-INF/views/auth/import/crossref.html @@ -0,0 +1,124 @@ + + + + + + + + + + + Import from Crossref + + + x + Import started! Head over to the job list to see the status of your import. + + + x + Import Failed! Error - + + + + + + Search + + + + + Results + + + + + Import into Group: + + + + + + Import Selected + + + + + + + + + diff --git a/citesphere/src/main/webapp/WEB-INF/views/layouts/main.html b/citesphere/src/main/webapp/WEB-INF/views/layouts/main.html index db0d1104e..7c5ff4c86 100644 --- a/citesphere/src/main/webapp/WEB-INF/views/layouts/main.html +++ b/citesphere/src/main/webapp/WEB-INF/views/layouts/main.html @@ -90,6 +90,8 @@ Import from File + Import + from CrossRef See all Imports diff --git a/citesphere/src/test/java/edu/asu/diging/citesphere/core/service/impl/ImportCrossrefJobManagerTest.java b/citesphere/src/test/java/edu/asu/diging/citesphere/core/service/impl/ImportCrossrefJobManagerTest.java new file mode 100644 index 000000000..8415d9191 --- /dev/null +++ b/citesphere/src/test/java/edu/asu/diging/citesphere/core/service/impl/ImportCrossrefJobManagerTest.java @@ -0,0 +1,114 @@ +package edu.asu.diging.citesphere.core.service.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.OffsetDateTime; +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import edu.asu.diging.citesphere.core.exceptions.GroupDoesNotExistException; +import edu.asu.diging.citesphere.core.exceptions.MessageCreationException; +import edu.asu.diging.citesphere.core.kafka.IKafkaRequestProducer; +import edu.asu.diging.citesphere.core.model.jobs.IImportCrossrefJob; +import edu.asu.diging.citesphere.core.model.jobs.JobStatus; +import edu.asu.diging.citesphere.core.model.jobs.impl.ImportCrossrefJob; +import edu.asu.diging.citesphere.core.repository.jobs.ImportCrossrefJobRepository; +import edu.asu.diging.citesphere.core.service.IGroupManager; +import edu.asu.diging.citesphere.core.service.jobs.impl.ImportCrossrefJobManager; +import edu.asu.diging.citesphere.core.service.jwt.IJwtTokenService; +import edu.asu.diging.citesphere.messages.model.KafkaJobMessage; +import edu.asu.diging.citesphere.model.bib.ICitationGroup; +import edu.asu.diging.citesphere.user.IUser; + +@RunWith(MockitoJUnitRunner.class) +public class ImportCrossrefJobManagerTest { + + @InjectMocks + private ImportCrossrefJobManager importCrossrefJobManager; + + @Mock + private IGroupManager groupManager; + + @Mock + private ImportCrossrefJobRepository jobRepo; + + @Mock + private IKafkaRequestProducer kafkaProducer; + + @Mock + private IJwtTokenService tokenService; + + @Mock + private IUser user; + + @Mock + private ICitationGroup group; + private List dois; + + @Before + public void setup() { + ReflectionTestUtils.setField(importCrossrefJobManager, "jobPageSize", 10); + doReturn("username").when(user).getUsername(); + dois = Arrays.asList("10.1000/xyz123", "10.1000/xyz456"); + } + + @Test + public void test_createJob_success() throws Exception { + String groupId = "groupId"; + List dois = Arrays.asList("doi1", "doi2"); + doReturn(group).when(groupManager).getGroup(user, groupId); + doReturn("jobToken").when(tokenService).generateJobApiToken(any(ImportCrossrefJob.class)); + + IImportCrossrefJob job = importCrossrefJobManager.createJob(user, groupId, dois); + + assertNotNull(job); + assertEquals(user.getUsername(), job.getUsername()); + assertEquals(dois, job.getDois()); + assertEquals(groupId, job.getCitationGroup()); + assertEquals(JobStatus.PREPARED, job.getStatus()); + verify(jobRepo, times(1)).save(any(ImportCrossrefJob.class)); + verify(kafkaProducer).sendRequest(any(KafkaJobMessage.class), anyString()); + } + + @Test + public void test_createJob_groupDoesNotExist() { + String groupId = "groupId"; + List dois = Arrays.asList("doi1", "doi2"); + doReturn(null).when(groupManager).getGroup(user, groupId); + + GroupDoesNotExistException exception = assertThrows(GroupDoesNotExistException.class, () -> importCrossrefJobManager.createJob(user, groupId, dois)); + assertNotNull(exception); + } + + @Test + public void test_createJob_messageCreationException() throws Exception { + String groupId = "groupId"; + when(groupManager.getGroup(user, groupId)).thenReturn(group); + when(tokenService.generateJobApiToken(any(ImportCrossrefJob.class))).thenReturn("token"); + doThrow(new MessageCreationException("Error creating message")).when(kafkaProducer).sendRequest(any(KafkaJobMessage.class), anyString()); + + IImportCrossrefJob job = importCrossrefJobManager.createJob(user, groupId, dois); + + assertNotNull(job); + assertEquals(JobStatus.FAILURE, job.getStatus()); + assertEquals(1, job.getPhases().size()); + verify(jobRepo, times(2)).save(any(ImportCrossrefJob.class)); + } +} \ No newline at end of file diff --git a/citesphere/src/test/java/edu/asu/diging/citesphere/web/user/jobs/ImportCrossrefControllerTest.java b/citesphere/src/test/java/edu/asu/diging/citesphere/web/user/jobs/ImportCrossrefControllerTest.java new file mode 100644 index 000000000..1d86d8fb7 --- /dev/null +++ b/citesphere/src/test/java/edu/asu/diging/citesphere/web/user/jobs/ImportCrossrefControllerTest.java @@ -0,0 +1,149 @@ +package edu.asu.diging.citesphere.web.user.jobs; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Matchers.anyList; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.security.Principal; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.ui.Model; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import edu.asu.diging.citesphere.core.exceptions.GroupDoesNotExistException; +import edu.asu.diging.citesphere.core.service.ICitationManager; +import edu.asu.diging.citesphere.core.service.crossref.CrossrefService; +import edu.asu.diging.citesphere.core.service.jobs.IImportCrossrefJobManager; +import edu.asu.diging.citesphere.core.user.IUserManager; +import edu.asu.diging.citesphere.model.bib.ICitationGroup; +import edu.asu.diging.citesphere.user.IUser; +import edu.asu.diging.crossref.exception.RequestFailedException; +import edu.asu.diging.crossref.model.Item; + +@RunWith(MockitoJUnitRunner.class) +public class ImportCrossrefControllerTest { + + @InjectMocks + private ImportCrossrefController importCrossrefController; + + @Mock + private CrossrefService crossrefService; + + @Mock + private ICitationManager citationManager; + + @Mock + private IUserManager userManager; + + @Mock + private IImportCrossrefJobManager jobManager; + + @Mock + private Model model; + + @Mock + private Principal principal; + + @Mock + private Authentication authentication; + + @Mock + private RedirectAttributes redirectAttrs; + + @Mock + private IUser user; + + @Before + public void setUp() { + when(principal.getName()).thenReturn("testuser"); + when(userManager.findByUsername("testuser")).thenReturn(user); + } + + @Test + public void test_get_successs() { + List groups = Arrays.asList(mock(ICitationGroup.class), mock(ICitationGroup.class)); + when(citationManager.getGroups(user)).thenReturn(groups); + + String viewName = importCrossrefController.get(model, principal); + + assertEquals("auth/import/crossref", viewName); + verify(model).addAttribute(eq("groups"), anyList()); + } + + @Test + public void test_search_success() throws RequestFailedException, IOException { + String query = "sample query"; + int page = 1; + List items = List.of(mock(Item.class), mock(Item.class)); + when(crossrefService.search(query, page)).thenReturn(items); + + ResponseEntity> response = importCrossrefController.search(query, page); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(items, response.getBody()); + } + + @Test + public void test_search_failure() throws RequestFailedException, IOException { + String query = "sample query"; + int page = 1; + when(crossrefService.search(query, page)).thenThrow(new IOException("IO Exception")); + + ResponseEntity> response = importCrossrefController.search(query, page); + + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); + assertNull(response.getBody()); + } + + @Test + public void test_post_success() throws GroupDoesNotExistException { + String groupId = "groupId"; + List dois = Arrays.asList("doi1", "doi2"); + when(authentication.getPrincipal()).thenReturn(user); + + ResponseEntity> responseEntity = importCrossrefController.post(authentication, groupId, dois, redirectAttrs); + + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + Map responseBody = responseEntity.getBody(); + assertNotNull(responseBody); + assertEquals(true, responseBody.get("show_alert")); + assertEquals("success", responseBody.get("alert_type")); + assertEquals("Import in progress.", responseBody.get("alert_msg")); + verify(jobManager).createJob(user, groupId, dois); + } + + @Test + public void test_post_groupDoesNotExistException() throws GroupDoesNotExistException { + String groupId = "groupId"; + List dois = Arrays.asList("doi1", "doi2"); + when(authentication.getPrincipal()).thenReturn(user); + doThrow(new GroupDoesNotExistException("Group does not exist")).when(jobManager).createJob(user, groupId, dois); + + ResponseEntity> responseEntity = importCrossrefController.post(authentication, groupId, dois, redirectAttrs); + + assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); + Map responseBody = responseEntity.getBody(); + assertNotNull(responseBody); + assertEquals(true, responseBody.get("show_alert")); + assertEquals("danger", responseBody.get("alert_type")); + assertEquals("Group does not exist", responseBody.get("alert_msg")); + } +}
+ Import into Group: + + + +
+ Import Selected +