Commit bd598351 authored by Maxym Borodenko's avatar Maxym Borodenko

API for fetching audit logs of Accession and Cooperator

parent a67d09ac
/*
* Copyright 2020 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gringlobal.api;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.genesys.blocks.auditlog.model.AuditLog;
import org.gringlobal.custom.json.converter.JsonCooperatorConverter;
/**
* @author Maxym Borodenko
*/
public class WrappedAuditLog extends AuditLog {
public WrappedAuditLog(AuditLog auditLog) {
super(auditLog);
}
@JsonSerialize(converter = JsonCooperatorConverter.class)
@Override
public Long getCreatedBy() {
return super.getCreatedBy();
}
}
......@@ -18,9 +18,14 @@ package org.gringlobal.api.v1.impl;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.ArrayUtils;
import org.genesys.blocks.auditlog.model.AuditLog;
import org.genesys.blocks.auditlog.model.filters.AuditLogFilter;
import org.genesys.blocks.auditlog.service.AuditTrailService;
import org.genesys.blocks.model.JsonViews;
import org.gringlobal.api.WrappedAuditLog;
import org.gringlobal.api.v1.ApiBaseController;
import org.gringlobal.api.v1.FilteredCRUDController;
import org.gringlobal.api.v1.FilteredPage;
......@@ -37,6 +42,8 @@ import org.gringlobal.service.AccessionService.AcquisitionData;
import org.gringlobal.service.filter.AccessionActionFilter;
import org.gringlobal.service.filter.AccessionFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
......@@ -67,6 +74,9 @@ public class AccessionController extends FilteredCRUDController<Accession, Acces
@Autowired
private AccessionActionService accessionActionService;
@Autowired
private AuditTrailService auditService;
@Override
protected Class<AccessionFilter> filterType() {
return AccessionFilter.class;
......@@ -114,6 +124,23 @@ public class AccessionController extends FilteredCRUDController<Accession, Acces
return crudService.getAccessionDetails(crudService.get(id));
}
/**
* Retrieve a list of audit logs for the specified accession
*
* @param accessionId the accessionId
* @param page the page request
* @return the list of all log entries
*/
@GetMapping("/auditlog/{id}")
public Page<WrappedAuditLog> accessionAuditLogs(@PathVariable(value = "id") final Long accessionId, @Parameter(hidden = true) final Pagination page) {
var filter = new AuditLogFilter();
filter.entityId = crudService.get(accessionId).getId();
filter.classname = Accession.class.getName();
Page<AuditLog> auditLogs = auditService.listAuditLogs(filter, page.toPageRequest(100));
var content = auditLogs.stream().map(WrappedAuditLog::new).collect(Collectors.toList());
return new PageImpl<>(content, auditLogs.getPageable(), auditLogs.getTotalElements());
}
@PostMapping(value = "/action/start", produces = { MediaType.APPLICATION_JSON_VALUE })
@Operation(operationId = "startActions", description = "Start accession actions", summary = "Start actions")
......
......@@ -16,7 +16,12 @@
package org.gringlobal.api.v1.impl;
import java.io.IOException;
import java.util.stream.Collectors;
import org.genesys.blocks.auditlog.model.AuditLog;
import org.genesys.blocks.auditlog.model.filters.AuditLogFilter;
import org.genesys.blocks.auditlog.service.AuditTrailService;
import org.gringlobal.api.WrappedAuditLog;
import org.gringlobal.api.v1.ApiBaseController;
import org.gringlobal.api.v1.FilteredCRUDController;
import org.gringlobal.api.v1.FilteredPage;
......@@ -26,8 +31,12 @@ import org.gringlobal.model.Cooperator;
import org.gringlobal.model.QCooperator;
import org.gringlobal.service.CooperatorService;
import org.gringlobal.service.filter.CooperatorFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
......@@ -47,6 +56,9 @@ public class CooperatorController extends FilteredCRUDController<Cooperator, Coo
/** The Constant API_URL. */
public static final String API_URL = ApiBaseController.APIv1_BASE + "/cooperator";
@Autowired
private AuditTrailService auditService;
@Override
protected OrderSpecifier<?>[] defaultSort() {
return new OrderSpecifier[] { QCooperator.cooperator.id.asc() };
......@@ -57,6 +69,24 @@ public class CooperatorController extends FilteredCRUDController<Cooperator, Coo
return CooperatorFilter.class;
}
/**
* Retrieve a list of audit logs for the specified cooperator
*
* @param cooperatorId the cooperatorId
* @param page the page request
* @return the list of all log entries
*/
@GetMapping("/auditlog/{id}")
public Page<WrappedAuditLog> cooperatorAuditLogs(@PathVariable(value = "id") final Long cooperatorId, @Parameter(hidden = true) final Pagination page) {
var filter = new AuditLogFilter();
filter.entityId = crudService.get(cooperatorId).getId();
filter.classname = Cooperator.class.getName();
Page<AuditLog> auditLogs = auditService.listAuditLogs(filter, page.toPageRequest(100));
var content = auditLogs.stream().map(WrappedAuditLog::new).collect(Collectors.toList());
return new PageImpl<>(content, auditLogs.getPageable(), auditLogs.getTotalElements());
}
@Override
public Cooperator create(@RequestBody Cooperator entity) {
return super.create(entity);
......
......@@ -22,19 +22,18 @@ import java.util.stream.Collectors;
import javax.validation.Valid;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.apache.commons.lang3.ArrayUtils;
import org.genesys.blocks.auditlog.model.AuditLog;
import org.genesys.blocks.auditlog.model.filters.AuditLogFilter;
import org.genesys.blocks.auditlog.service.AuditTrailService;
import org.genesys.filerepository.InvalidRepositoryFileDataException;
import org.genesys.filerepository.InvalidRepositoryPathException;
import org.gringlobal.api.WrappedAuditLog;
import org.gringlobal.api.v1.ApiBaseController;
import org.gringlobal.api.v1.FilteredCRUDController;
import org.gringlobal.api.v1.FilteredPage;
import org.gringlobal.api.v1.Pagination;
import org.gringlobal.custom.elasticsearch.SearchException;
import org.gringlobal.custom.json.converter.JsonCooperatorConverter;
import org.gringlobal.model.AccessionInvAttach;
import org.gringlobal.model.Inventory;
import org.gringlobal.model.InventoryAction;
......@@ -208,16 +207,4 @@ public class InventoryController extends FilteredCRUDController<Inventory, Inven
return attachmentFileService.removeFile(crudService.get(inventoryId), attachmentId);
}
private static class WrappedAuditLog extends AuditLog {
public WrappedAuditLog(AuditLog auditLog) {
super(auditLog);
}
@JsonSerialize(converter = JsonCooperatorConverter.class)
@Override
public Long getCreatedBy() {
return super.getCreatedBy();
}
}
}
......@@ -229,7 +229,7 @@ public class AccessionControllerTest extends AbstractApiV1Test {
for (int i = 0; i < 10; i++) {
Accession accession = new Accession();
accession.setAccessionNumberPart1("T2020");
accession.setAccessionNumberPart2(-1l);
accession.setAccessionNumberPart2(-1L);
accession.setTaxonomySpecies(new TaxonomySpecies(taxonomySpecies.getId()));
List<AccessionInvName> names = Lists.newArrayList(new AccessionInvName(), new AccessionInvName(), new AccessionInvName());
......@@ -260,7 +260,7 @@ public class AccessionControllerTest extends AbstractApiV1Test {
source.setSourceTypeCode("DEVELOPED");
source.setSourceDate(new Date());
source.setSourceDateCode("YYYY-MM-DD");
cooperators = Lists.newArrayList(new Cooperator(3l));
cooperators = Lists.newArrayList(new Cooperator(3L));
source.setCooperators(cooperators);
acquisitionBatch.sources.add(source);
......@@ -293,9 +293,47 @@ public class AccessionControllerTest extends AbstractApiV1Test {
// return true;
// });
assertThat(accessionRepository.count(), is(10l));
assertThat(inventoryRepository.count(), is(20l));
assertThat(accessionSourceRepository.count(), is(20l));
assertThat(accessionInvNameRepository.count(), is(30l));
assertThat(accessionRepository.count(), is(10L));
assertThat(inventoryRepository.count(), is(20L));
assertThat(accessionSourceRepository.count(), is(20L));
assertThat(accessionInvNameRepository.count(), is(30L));
}
@Test
public void testAccessionAuditLogs() throws Exception {
// build accession
Accession a = new Accession();
a.setAccessionNumberPart1("AccessionNumberPart1");
a.setIsBackedUp(TRUE);
a.setIsCore(TRUE);
a.setIsWebVisible(TRUE);
a.setStatusCode(ACCESSION_STATUS_CODE);
a.setTaxonomySpecies(addTaxonomySpeciesToDB());
// add accession to DB
Accession savedAccession = accessionService.create(a);
savedAccession.setNote("test note");
Accession updatedAccession = accessionService.update(savedAccession);
assertThat(updatedAccession.getId(), is(savedAccession.getId()));
/*@formatter:off*/
mockMvc
.perform(get(AccessionController.API_URL.concat("/auditlog/{id}?s=propertyName"), savedAccession.getId())
.contentType(MediaType.APPLICATION_JSON)
)
.andDo(org.springframework.test.web.servlet.result.MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.totalElements", greaterThan(0)))
.andExpect(jsonPath("$.sort[0]", not(nullValue())))
.andExpect(jsonPath("$.sort[0].direction", is("ASC")))
.andExpect(jsonPath("$.sort[0].property", is("propertyName")))
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content[0]").isMap())
.andExpect(jsonPath("$.content[0].createdBy").isString())
.andExpect(jsonPath("$.content[0].logDate").exists())
.andExpect(jsonPath("$.content[0].classPk").isMap())
;
/*@formatter:on*/
}
}
......@@ -21,6 +21,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import com.google.common.collect.Sets;
import org.genesys.blocks.auditlog.model.AuditAction;
import org.gringlobal.api.v1.CRUDController;
import org.gringlobal.api.v1.impl.CooperatorController;
import org.gringlobal.model.Cooperator;
......@@ -252,6 +253,47 @@ public class CooperatorControllerTest extends AbstractApiV1Test {
/*@formatter:on*/
}
@Test
public void cooperatorAuditLogsTest() throws Exception {
Cooperator currentCooperator = cooperatorRepository.findAll().get(0);
assertThat(currentCooperator, is(notNullValue()));
SysLang sysLang = sysLangRepository.findAll().get(0);
assertThat(sysLang, is(notNullValue()));
Cooperator saved = service.create(build(null, STATUS_CODE_DECEASED, sysLang, NOTE_1, ADDRESS_LINE1_2));
assertThat(saved, is(notNullValue()));
assertThat(cooperatorRepository.count(), equalTo(4L));
saved.setNote(NOTE_2);
Cooperator updated = service.update(saved);
assertThat(saved.getId(), is(updated.getId()));
/*@formatter:off*/
mockMvc
.perform(get(CooperatorController.API_URL.concat("/auditlog/{id}?s=propertyName"), saved.getId())
.contentType(MediaType.APPLICATION_JSON)
)
// .andDo(org.springframework.test.web.servlet.result.MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.totalElements", is(1)))
.andExpect(jsonPath("$.sort[0]", not(nullValue())))
.andExpect(jsonPath("$.sort[0].direction", is("ASC")))
.andExpect(jsonPath("$.sort[0].property", is("propertyName")))
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content[0]").isMap())
.andExpect(jsonPath("$.content[0].createdBy").isString())
.andExpect(jsonPath("$.content[0].logDate").exists())
.andExpect(jsonPath("$.content[0].classPk").isMap())
.andExpect(jsonPath("$.content[0].classPk.classname", is(Cooperator.class.getCanonicalName())))
.andExpect(jsonPath("$.content[0].entityId", is(saved.getId().intValue())))
.andExpect(jsonPath("$.content[0].propertyName", is("note")))
.andExpect(jsonPath("$.content[0].previousState", is(NOTE_1)))
.andExpect(jsonPath("$.content[0].newState", is(NOTE_2)))
.andExpect(jsonPath("$.content[0].action", is(AuditAction.UPDATE.name())))
;
/*@formatter:on*/
}
@Test
public void removeTest() throws Exception {
Cooperator currentCooperator = cooperatorRepository.findAll().get(0);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment