v3_issue_811: Trying to implement search feature for validation summary report. Added a new service. Will start moving bulky functions in the controller to the summary service class.
Some checks are pending
Dotnet Provisioner Unit Tests / Restore and Run Unit Tests (ubuntu-latest) (push) Waiting to run
Dotnet Provisioner Unit Tests / Restore and Run Unit Tests (windows-2022) (push) Waiting to run
Dotnet Provisioner Unit Tests / Evaluate Tests (push) Blocked by required conditions
HIRS Build and Unit Test / ACA_Provisioner_Unit_Tests (push) Waiting to run
HIRS System Tests / DockerTests (push) Waiting to run

This commit is contained in:
ThatSilentCoder 2025-04-10 17:56:35 -04:00
parent 1245917fed
commit e1ee5a0ce3
10 changed files with 493 additions and 303 deletions

View File

@ -49,11 +49,11 @@ public class CertificateService {
private final EntityManager entityManager;
/**
* Con
* Constructor for the Certificate Service.
*
* @param certificateRepository
* @param componentResultRepository
* @param entityManager
* @param certificateRepository certificateRepository
* @param componentResultRepository componentResultRepository
* @param entityManager entityManager
*/
@Autowired
public CertificateService(final CertificateRepository certificateRepository,
@ -65,6 +65,9 @@ public class CertificateService {
}
/**
* Takes the provided column names, the search term that the user entered and attempts to find
* certificates whose field values matches the provided search term.
*
* @param entityClass generic entity class
* @param searchableColumns list of the searchable column name
* @param searchText text that was input in the search textbox
@ -73,11 +76,12 @@ public class CertificateService {
* @param <T> generic entity class
* @return page full of the generic certificates.
*/
public <T extends Certificate> Page<T> findBySearchableColumnsAndArchiveFlag(Class<T> entityClass,
List<String> searchableColumns,
String searchText,
Boolean archiveFlag,
Pageable pageable) {
public <T extends Certificate> Page<T> findCertificatesBySearchableColumnsAndArchiveFlag(
Class<T> entityClass,
final List<String> searchableColumns,
final String searchText,
Boolean archiveFlag,
Pageable pageable) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<T> query = criteriaBuilder.createQuery(entityClass);
Root<T> certificate = query.from(entityClass);
@ -88,6 +92,12 @@ public class CertificateService {
if (!StringUtils.isBlank(searchText)) {
// Dynamically loop through columns and create LIKE conditions for each searchable column
for (String columnName : searchableColumns) {
// todo
if (columnName.contains(".")) {
}
Predicate predicate =
criteriaBuilder.like(criteriaBuilder.lower(certificate.get(columnName)),
"%" + searchText.toLowerCase() + "%");

View File

@ -0,0 +1,101 @@
package hirs.attestationca.persist.service;
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidationSummary;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
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.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* Service layer class that handles the storage and retrieval of validation reports.
*/
@Service
@Log4j2
public class ValidationSummaryReportsService {
private final EntityManager entityManager;
@Autowired
public ValidationSummaryReportsService(EntityManager entityManager) {
this.entityManager = entityManager;
}
public static Path<?> getNestedPath(Root<?> root, CriteriaBuilder cb, String... fieldNames) {
Path<?> path = root;
for (String fieldName : fieldNames) {
path = path.get(fieldName);
}
return path;
}
/**
* Takes the provided column names, the search term that the user entered and attempts to find
* validation summaries whose field values matches the provided search term.
*
* @param searchableColumns list of the searchable column name
* @param searchText text that was input in the search textbox
* @param archiveFlag archive flag
* @param pageable pageable
* @return page full of the validation summaries.
*/
public Page<SupplyChainValidationSummary> findValidationReportsBySearchableColumnsAndArchiveFlag(
final List<String> searchableColumns,
final String searchText,
Boolean archiveFlag,
Pageable pageable) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<SupplyChainValidationSummary> query =
criteriaBuilder.createQuery(SupplyChainValidationSummary.class);
Root<SupplyChainValidationSummary> supplyChainValidationSummaryRoot =
query.from(SupplyChainValidationSummary.class);
List<Predicate> predicates = new ArrayList<>();
// Dynamically add search conditions for each field that should be searchable
if (!StringUtils.isBlank(searchText)) {
// Dynamically loop through columns and create LIKE conditions for each searchable column
for (String columnName : searchableColumns) {
// todo
if (columnName.contains(".")) {
}
Predicate predicate =
criteriaBuilder.like(
criteriaBuilder.lower(supplyChainValidationSummaryRoot.get(columnName)),
"%" + searchText.toLowerCase() + "%");
predicates.add(predicate);
}
}
Predicate likeConditions = criteriaBuilder.or(predicates.toArray(new Predicate[0]));
// Add archiveFlag condition if specified
query.where(criteriaBuilder.and(likeConditions,
criteriaBuilder.equal(supplyChainValidationSummaryRoot.get("archiveFlag"), archiveFlag)));
// Apply pagination
TypedQuery<SupplyChainValidationSummary> typedQuery = entityManager.createQuery(query);
int totalRows = typedQuery.getResultList().size(); // Get the total count for pagination
typedQuery.setFirstResult((int) pageable.getOffset());
typedQuery.setMaxResults(pageable.getPageSize());
// Wrap the result in a Page object to return pagination info
List<SupplyChainValidationSummary> resultList = typedQuery.getResultList();
return new PageImpl<>(resultList, pageable, totalRows);
}
}

View File

@ -97,7 +97,7 @@ public class DevicePageController extends PageController<NoPageParams> {
produces = MediaType.APPLICATION_JSON_VALUE)
public DataTableResponse<HashMap<String, Object>> getTableData(
final DataTableInput input) {
log.debug("Receiving request to for device list");
log.debug("Received request to for device list");
String orderColumnName = input.getOrderColumnName();
log.debug("Ordering on column: {}", orderColumnName);

View File

@ -98,7 +98,7 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
produces = MediaType.APPLICATION_JSON_VALUE)
public DataTableResponse<EndorsementCredential> getEndorsementCredentialsTableData(
final DataTableInput input) {
log.info("Receiving request to display list of endorsement credentials");
log.info("Received request to display list of endorsement credentials");
log.debug("Request received a datatable input object for the endorsement credentials page: {}",
input);
@ -107,8 +107,8 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
log.debug("Ordering on column: {}", orderColumnName);
String searchText = input.getSearch().getValue();
List<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
final String searchText = input.getSearch().getValue();
final List<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
int currentPage = input.getStart() / input.getLength();
Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
@ -121,7 +121,7 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
pagedResult = this.endorsementCredentialRepository.findByArchiveFlag(false, pageable);
} else {
pagedResult =
this.certificateService.findBySearchableColumnsAndArchiveFlag(
this.certificateService.findCertificatesBySearchableColumnsAndArchiveFlag(
EndorsementCredential.class,
searchableColumns,
searchText,
@ -155,7 +155,7 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
@RequestParam final String id,
final HttpServletResponse response)
throws IOException {
log.info("Receiving request to download endorsement credential id {}", id);
log.info("Received request to download endorsement credential id {}", id);
try {
UUID uuid = UUID.fromString(id);
@ -208,7 +208,7 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
@GetMapping("/bulk-download")
public void bulkDownloadEndorsementCredentials(final HttpServletResponse response)
throws IOException {
log.info("Receiving request to download all endorsement credentials");
log.info("Received request to download all endorsement credentials");
final String fileName = "endorsement_certificates.zip";
final String singleFileName = "Endorsement_Certificates";
@ -243,7 +243,7 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
@RequestParam("file") final MultipartFile[] files,
final RedirectAttributes attr) throws URISyntaxException {
log.info("Receiving request to upload one or more endorsement credentials");
log.info("Received request to upload one or more endorsement credentials");
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
@ -286,7 +286,7 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
public RedirectView deleteEndorsementCredential(
@RequestParam final String id,
final RedirectAttributes attr) throws URISyntaxException {
log.info("Receiving request to delete endorsement credential id {}", id);
log.info("Received request to delete endorsement credential id {}", id);
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();

View File

@ -98,7 +98,7 @@ public class IDevIdCertificatePageController extends PageController<NoPageParams
public DataTableResponse<IDevIDCertificate> getIDevIdCertificatesTableData(
final DataTableInput input) {
log.info("Receiving request to display list of idevid certificates");
log.info("Received request to display list of idevid certificates");
log.debug("Request received a datatable input object for the idevid certificates page: {}", input);
// attempt to get the column property based on the order index.
@ -106,8 +106,8 @@ public class IDevIdCertificatePageController extends PageController<NoPageParams
log.debug("Ordering on column: {}", orderColumnName);
String searchText = input.getSearch().getValue();
List<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
final String searchText = input.getSearch().getValue();
final List<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
int currentPage = input.getStart() / input.getLength();
Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
@ -120,7 +120,7 @@ public class IDevIdCertificatePageController extends PageController<NoPageParams
this.iDevIDCertificateRepository.findByArchiveFlag(false, pageable);
} else {
pagedResult =
this.certificateService.findBySearchableColumnsAndArchiveFlag(
this.certificateService.findCertificatesBySearchableColumnsAndArchiveFlag(
IDevIDCertificate.class,
searchableColumns,
searchText,
@ -154,7 +154,7 @@ public class IDevIdCertificatePageController extends PageController<NoPageParams
@RequestParam final String id,
final HttpServletResponse response)
throws IOException {
log.info("Receiving request to download idevid certificate id {}", id);
log.info("Received request to download idevid certificate id {}", id);
try {
UUID uuid = UUID.fromString(id);
@ -205,7 +205,7 @@ public class IDevIdCertificatePageController extends PageController<NoPageParams
@GetMapping("/bulk-download")
public void bulkDownloadIDevIdCertificates(final HttpServletResponse response)
throws IOException {
log.info("Receiving request to download all idevid certificates");
log.info("Received request to download all idevid certificates");
final String fileName = "idevid_certificates.zip";
final String singleFileName = "IDevID_Certificates";
@ -240,7 +240,7 @@ public class IDevIdCertificatePageController extends PageController<NoPageParams
@RequestParam("file") final MultipartFile[] files,
final RedirectAttributes attr) throws URISyntaxException {
log.info("Receiving request to upload one or more idevid certificates");
log.info("Received request to upload one or more idevid certificates");
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
@ -284,7 +284,7 @@ public class IDevIdCertificatePageController extends PageController<NoPageParams
public RedirectView deleteIdevIdCertificate(
@RequestParam final String id,
final RedirectAttributes attr) throws URISyntaxException {
log.info("Receiving request to delete idevid certificate id {}", id);
log.info("Received request to delete idevid certificate id {}", id);
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();

View File

@ -48,7 +48,7 @@ import java.util.zip.ZipOutputStream;
@Log4j2
@Controller
@RequestMapping("/HIRS_AttestationCAPortal/portal/certificate-request/issued-certificates")
public class IssuedCertificateController extends PageController<NoPageParams> {
public class IssuedCertificatePageController extends PageController<NoPageParams> {
private static final String ISSUED_CERTIFICATES = "issued-certificates";
@ -62,7 +62,7 @@ public class IssuedCertificateController extends PageController<NoPageParams> {
* @param certificateService certificateService
*/
@Autowired
public IssuedCertificateController(
public IssuedCertificatePageController(
final IssuedCertificateRepository issuedCertificateRepository,
final CertificateService certificateService) {
super(Page.TRUST_CHAIN);
@ -96,7 +96,7 @@ public class IssuedCertificateController extends PageController<NoPageParams> {
produces = MediaType.APPLICATION_JSON_VALUE)
public DataTableResponse<IssuedAttestationCertificate> getIssuedCertificatesTableData(
final DataTableInput input) {
log.info("Receiving request to display list of issued attestation certificates");
log.info("Received request to display list of issued attestation certificates");
log.debug("Request received a datatable input object for the issued attestation certificate page: "
+ "{}", input);
@ -105,8 +105,8 @@ public class IssuedCertificateController extends PageController<NoPageParams> {
log.debug("Ordering on column: {}", orderColumnName);
String searchText = input.getSearch().getValue();
List<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
final String searchText = input.getSearch().getValue();
final List<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
int currentPage = input.getStart() / input.getLength();
Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
@ -119,7 +119,7 @@ public class IssuedCertificateController extends PageController<NoPageParams> {
this.issuedCertificateRepository.findByArchiveFlag(false, pageable);
} else {
pagedResult =
this.certificateService.findBySearchableColumnsAndArchiveFlag(
this.certificateService.findCertificatesBySearchableColumnsAndArchiveFlag(
IssuedAttestationCertificate.class,
searchableColumns,
searchText,
@ -153,7 +153,7 @@ public class IssuedCertificateController extends PageController<NoPageParams> {
@RequestParam final String id,
final HttpServletResponse response)
throws IOException {
log.info("Receiving request to download issued certificate id {}", id);
log.info("Received request to download issued certificate id {}", id);
try {
UUID uuid = UUID.fromString(id);
@ -208,7 +208,7 @@ public class IssuedCertificateController extends PageController<NoPageParams> {
@GetMapping("/bulk-download")
public void bulkDownloadIssuedCertificates(final HttpServletResponse response)
throws IOException {
log.info("Receiving request to download all issued certificates");
log.info("Received request to download all issued certificates");
final String singleFileName = "Issued_Certificate";
final String fileName = "issued_certificates.zip";
@ -243,7 +243,7 @@ public class IssuedCertificateController extends PageController<NoPageParams> {
public RedirectView deleteIssuedCertificate(
@RequestParam final String id,
final RedirectAttributes attr) throws URISyntaxException {
log.info("Receiving request to delete issued attestation certificate id {}", id);
log.info("Received request to delete issued attestation certificate id {}", id);
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();

View File

@ -104,7 +104,7 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
produces = MediaType.APPLICATION_JSON_VALUE)
public DataTableResponse<PlatformCredential> getPlatformCredentialsTableData(
final DataTableInput input) {
log.info("Receiving request to display list of platform credentials");
log.info("Received request to display list of platform credentials");
log.debug("Request received a datatable input object for the platform credentials page: {}", input);
// attempt to get the column property based on the order index.
@ -112,8 +112,8 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
log.debug("Ordering on column: {}", orderColumnName);
String searchText = input.getSearch().getValue();
List<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
final String searchText = input.getSearch().getValue();
final List<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
int currentPage = input.getStart() / input.getLength();
Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
@ -127,7 +127,7 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
this.platformCertificateRepository.findByArchiveFlag(false, pageable);
} else {
pagedResult =
this.certificateService.findBySearchableColumnsAndArchiveFlag(
this.certificateService.findCertificatesBySearchableColumnsAndArchiveFlag(
PlatformCredential.class,
searchableColumns,
searchText,
@ -179,7 +179,7 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
@RequestParam final String id,
final HttpServletResponse response)
throws IOException {
log.info("Receiving request to download platform credential id {}", id);
log.info("Received request to download platform credential id {}", id);
try {
UUID uuid = UUID.fromString(id);
@ -232,7 +232,7 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
@GetMapping("/bulk-download")
public void bulkDownloadPlatformCredentials(final HttpServletResponse response)
throws IOException {
log.info("Receiving request to download all platform credentials");
log.info("Received request to download all platform credentials");
final String fileName = "platform_certificates.zip";
final String singleFileName = "Platform_Certificate";
@ -267,7 +267,7 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
@RequestParam("file") final MultipartFile[] files,
final RedirectAttributes attr) throws URISyntaxException {
log.info("Receiving request to upload one or more platform credentials");
log.info("Received request to upload one or more platform credentials");
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
@ -310,7 +310,7 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
public RedirectView deletePlatformCredential(
@RequestParam final String id,
final RedirectAttributes attr) throws URISyntaxException {
log.info("Receiving request to delete platform credential id {}", id);
log.info("Received request to delete platform credential id {}", id);
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
@ -350,6 +350,7 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
}
/**
* N
* Attempts to parse the provided file in order to create a Platform Credential.
*
* @param file file

View File

@ -136,7 +136,7 @@ public class TrustChainCertificatePageController extends PageController<NoPagePa
produces = MediaType.APPLICATION_JSON_VALUE)
public DataTableResponse<CertificateAuthorityCredential> getTrustChainCertificatesTableData(
final DataTableInput input) {
log.info("Receiving request to display list of trust chain certificates");
log.info("Received request to display list of trust chain certificates");
log.debug("Request received a datatable input object for the trust chain certificates page: {}",
input);
@ -145,8 +145,8 @@ public class TrustChainCertificatePageController extends PageController<NoPagePa
log.debug("Ordering on column: {}", orderColumnName);
String searchText = input.getSearch().getValue();
List<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
final String searchText = input.getSearch().getValue();
final List<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
int currentPage = input.getStart() / input.getLength();
Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
@ -160,7 +160,7 @@ public class TrustChainCertificatePageController extends PageController<NoPagePa
this.caCredentialRepository.findByArchiveFlag(false, pageable);
} else {
pagedResult =
this.certificateService.findBySearchableColumnsAndArchiveFlag(
this.certificateService.findCertificatesBySearchableColumnsAndArchiveFlag(
CertificateAuthorityCredential.class,
searchableColumns,
searchText,
@ -195,7 +195,7 @@ public class TrustChainCertificatePageController extends PageController<NoPagePa
@RequestParam final String id,
final HttpServletResponse response)
throws IOException {
log.info("Receiving request to download trust chain certificate {}", id);
log.info("Received request to download trust chain certificate {}", id);
try {
UUID uuid = UUID.fromString(id);
@ -252,7 +252,7 @@ public class TrustChainCertificatePageController extends PageController<NoPagePa
public void downloadAcaCertificate(final HttpServletResponse response)
throws IOException {
log.info("Receiving request to download the ACA server trust chain certificate");
log.info("Received request to download the ACA server trust chain certificate");
// Set filename for download.
response.setHeader("Content-Disposition", "attachment; filename=\"hirs-aca-cert.cer\"");
@ -273,7 +273,7 @@ public class TrustChainCertificatePageController extends PageController<NoPagePa
@GetMapping("/bulk-download")
public void bulkDownloadTrustChainCertificates(final HttpServletResponse response)
throws IOException {
log.info("Receiving request to download all trust chain certificates");
log.info("Received request to download all trust chain certificates");
final String fileName = "trust-chain.zip";
final String singleFileName = "ca-certificates";
@ -307,7 +307,7 @@ public class TrustChainCertificatePageController extends PageController<NoPagePa
@RequestParam("file") final MultipartFile[] files,
final RedirectAttributes attr) throws URISyntaxException {
log.info("Receiving request to upload one or more trust chain certificates");
log.info("Received request to upload one or more trust chain certificates");
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
@ -351,7 +351,7 @@ public class TrustChainCertificatePageController extends PageController<NoPagePa
public RedirectView deleteTrustChainCertificate(
@RequestParam final String id,
final RedirectAttributes attr) throws URISyntaxException {
log.info("Receiving request to delete trust chain certificate id {}", id);
log.info("Received request to delete trust chain certificate id {}", id);
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();

View File

@ -12,15 +12,17 @@ import hirs.attestationca.persist.entity.userdefined.SupplyChainValidationSummar
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2;
import hirs.attestationca.persist.service.ValidationSummaryReportsService;
import hirs.attestationca.portal.datatables.Column;
import hirs.attestationca.portal.datatables.DataTableInput;
import hirs.attestationca.portal.datatables.DataTableResponse;
import hirs.attestationca.portal.page.Page;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.params.NoPageParams;
import jakarta.persistence.EntityManager;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@ -47,6 +49,7 @@ import java.util.Enumeration;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Controller for the Validation Reports page.
@ -63,12 +66,12 @@ public class ValidationReportsPageController extends PageController<NoPageParams
+ "Model,SN,Verification Date,Device Status";
private static final String COMPONENT_COLUMN_HEADERS = "Component name,Component manufacturer,"
+ "Component model,Component SN,Issuer,Component status";
private final SupplyChainValidationSummaryRepository supplyChainValidatorSummaryRepository;
private final CertificateRepository certificateRepository;
private final DeviceRepository deviceRepository;
private final PlatformCertificateRepository platformCertificateRepository;
@Autowired(required = false)
private EntityManager entityManager;
private final ValidationSummaryReportsService validationSummaryReportsService;
/**
* Constructor providing the Page's display and routing specification.
@ -77,18 +80,21 @@ public class ValidationReportsPageController extends PageController<NoPageParams
* @param certificateRepository the certificate manager
* @param deviceRepository the device manager
* @param platformCertificateRepository the platform certificate manager
* @param validationSummaryReportsService the validation summary reports service
*/
@Autowired
public ValidationReportsPageController(
final SupplyChainValidationSummaryRepository supplyChainValidatorSummaryRepository,
final CertificateRepository certificateRepository,
final DeviceRepository deviceRepository,
final PlatformCertificateRepository platformCertificateRepository) {
final PlatformCertificateRepository platformCertificateRepository,
final ValidationSummaryReportsService validationSummaryReportsService) {
super(Page.VALIDATION_REPORTS);
this.supplyChainValidatorSummaryRepository = supplyChainValidatorSummaryRepository;
this.certificateRepository = certificateRepository;
this.deviceRepository = deviceRepository;
this.platformCertificateRepository = platformCertificateRepository;
this.validationSummaryReportsService = validationSummaryReportsService;
}
/**
@ -105,26 +111,46 @@ public class ValidationReportsPageController extends PageController<NoPageParams
}
/**
* Gets the list of validation summaries per the data table input query.
* Processes request to retrieve the collection of supply chain summary records that will be displayed
* on the validation reports page.
*
* @param input the data table query.
* @return the data table response containing the supply chain summary records
*/
@ResponseBody
@GetMapping(value = "/list", produces = MediaType.APPLICATION_JSON_VALUE)
public DataTableResponse<SupplyChainValidationSummary> getTableData(
public DataTableResponse<SupplyChainValidationSummary> getValidationReportsTableData(
final DataTableInput input) {
log.debug("Handling request for summary list: {}", input);
log.info("Received request to display list of validation reports");
log.debug("Request received a datatable input object for the validation reports page: {}", input);
// attempt to get the column property based on the order index.
String orderColumnName = input.getOrderColumnName();
log.debug("Ordering on column: {}", orderColumnName);
final String searchText = input.getSearch().getValue();
final List<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
FilteredRecordsList<SupplyChainValidationSummary> records = new FilteredRecordsList<>();
int currentPage = input.getStart() / input.getLength();
Pageable paging = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
org.springframework.data.domain.Page<SupplyChainValidationSummary> pagedResult =
supplyChainValidatorSummaryRepository.findByArchiveFlagFalse(paging);
Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
org.springframework.data.domain.Page<SupplyChainValidationSummary> pagedResult;
if (StringUtils.isBlank(searchText)) {
pagedResult =
this.supplyChainValidatorSummaryRepository.findByArchiveFlagFalse(pageable);
} else {
pagedResult =
this.validationSummaryReportsService.findValidationReportsBySearchableColumnsAndArchiveFlag(
searchableColumns,
searchText,
false,
pageable);
}
if (pagedResult.hasContent()) {
records.addAll(pagedResult.getContent());
@ -135,6 +161,7 @@ public class ValidationReportsPageController extends PageController<NoPageParams
records.setRecordsFiltered(supplyChainValidatorSummaryRepository.count());
log.info("Returning the size of the list of validation reports: {}", records.size());
return new DataTableResponse<>(records, input);
}
@ -145,11 +172,11 @@ public class ValidationReportsPageController extends PageController<NoPageParams
* @param response object
* @throws IOException thrown by BufferedWriter object
*/
@PostMapping("download")
@PostMapping("/download")
public void download(final HttpServletRequest request,
final HttpServletResponse response) throws IOException {
log.info("Downloading validation report");
log.info("Received request to download validation report");
String company = "";
String contractNumber = "";
Pattern pattern = Pattern.compile("^\\w*$");
@ -324,6 +351,18 @@ public class ValidationReportsPageController extends PageController<NoPageParams
bufferedWriter.flush();
}
/**
* Helper method that returns a list of column names that are searchable.
*
* @param columns columns
* @return searchable column names
*/
private List<String> findSearchableColumnsNames(final List<Column> columns) {
// Retrieve all searchable columns and collect their names into a list of strings.
return columns.stream().filter(Column::isSearchable).map(Column::getName)
.collect(Collectors.toList());
}
/**
* This method builds a JSON object from the system and component data in a
* validation report.
@ -339,7 +378,7 @@ public class ValidationReportsPageController extends PageController<NoPageParams
final String company,
final String contractNumber) {
JsonObject systemData = new JsonObject();
String deviceName = deviceRepository.findById(pc
String deviceName = deviceRepository.findById((pc)
.getDeviceId()).get().getName();
systemData.addProperty("Company", company);

View File

@ -8,249 +8,288 @@
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script
type="text/javascript"
src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"
></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Validation Reports</jsp:attribute>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Validation Reports</jsp:attribute>
<jsp:body>
<!-- text and icon resource variables -->
<c:set
var="passIcon"
value="${icons}/ic_checkbox_marked_circle_black_green_24dp.png"
/>
<c:set var="failIcon" value="${icons}/ic_error_red_24dp.png" />
<c:set var="errorIcon" value="${icons}/ic_error_black_24dp.png" />
<c:set
var="unknownIcon"
value="${icons}/ic_questionmark_circle_orange_24dp.png"
/>
<c:set var="passText" value="Validation Passed" />
<c:set var="failText" value="Validation Failed" />
<c:set var="errorText" value="Validation Error" />
<c:set var="unknownText" value="Unknown Validation Status" />
<jsp:body>
<!-- text and icon resource variables -->
<c:set var="passIcon" value="${icons}/ic_checkbox_marked_circle_black_green_24dp.png"/>
<c:set var="failIcon" value="${icons}/ic_error_red_24dp.png"/>
<c:set var="errorIcon" value="${icons}/ic_error_black_24dp.png"/>
<c:set var="unknownIcon" value="${icons}/ic_questionmark_circle_orange_24dp.png"/>
<c:set var="passText" value="Validation Passed"/>
<c:set var="failText" value="Validation Failed"/>
<c:set var="errorText" value="Validation Error"/>
<c:set var="unknownText" value="Unknown Validation Status"/>
<form:form id="download" method="POST" action="${portal}/validation-reports/download">
Download Validation Reports
<my:download-info id="validationReportsDownload" label="Download Validation Reports">
<label>Company<input id="company" type="text" pattern="^\w*$"
<form:form
id="download"
method="POST"
action="${portal}/validation-reports/download"
>
Download Validation Reports
<my:download-info
id="validationReportsDownload"
label="Download Validation Reports"
>
<label>Company<input id="company" type="text" pattern="^\w*$"
title="Letters, numbers, and spaces only" name="company" /></label>
<label>Contract #<input id="contract" type="text" pattern="^\w*$"
<label>Contract #<input id="contract" type="text" pattern="^\w*$"
title="Letters, numbers, and spaces only" name="contract" /></label>
<br>
<label>Date range start<input id="dateStart" type="date" name="dateStart" /></label>
<label>Date range end<input id="dateEnd" type="date" name="dateEnd" /></label>
</my:download-info>
</form:form>
<br>
<label>Date range start<input id="dateStart" type="date" name="dateStart" /></label>
<label>Date range end<input id="dateEnd" type="date" name="dateEnd" /></label>
</my:download-info>
</form:form>
<div class="aca-data-table">
<table id="reportTable" class="display" width="100%">
<thead>
<tr>
<th rowspan="2">Result</th>
<th rowspan="2">Timestamp</th>
<th rowspan="2">Device</th>
<th colspan="3">Credential Validations</th>
</tr>
<tr>
<th style="text-align:center">Endorsement</th>
<th style="text-align:center">Platform</th>
<th style="text-align:center">Firmware</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function () {
let url = portal + '/validation-reports/list';
let columns = [
{
data: 'overallValidationResult',
searchable: false,
render: function (data, type, full, meta) {
let html = '';
let unknownStatus = '<img class="icon" src="${unknownIcon}" title="${unknownText}"/>';
<div class="aca-data-table">
<table id="reportTable" class="display" width="100%">
<thead>
<tr>
<th rowspan="2">Result</th>
<th rowspan="2">Timestamp</th>
<th rowspan="2">Device</th>
<th colspan="3">Credential Validations</th>
</tr>
<tr>
<th style="text-align: center">Endorsement</th>
<th style="text-align: center">Platform</th>
<th style="text-align: center">Firmware</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function () {
let url = portal + "/validation-reports/list";
let columns = [
{
name: "overallValidationResult",
data: "overallValidationResult",
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
let html = "";
let unknownStatus =
'<img class="icon" src="${unknownIcon}" title="${unknownText}"/>';
// create status icon
let result = full.overallValidationResult;
let overallMessage = full.message;
if (result) {
switch (result) {
case "PASS":
html += '<img src="${passIcon}" title="${passText}"/>';
break;
case "FAIL":
html += '<img src="${failIcon}" title="' + overallMessage + '"/>';
break;
case "ERROR":
html += '<img src="${errorIcon}" title="' + overallMessage + '"/>';
break;
default:
html += unknownStatus;
break;
}
} else {
html += unknownStatus;
}
return html;
}
},
{
// Note: DB column is create_time, while the
// JSON property / java property is createTime. Need to sort
// on the field createTime, but the column's
// date source is create_time.
data: 'create_time',
name: 'createTime',
searchable: false,
render: function (data, type, full, meta) {
return formatCertificateDate(full.createTime);
}
},
{
// TODO render a link to a device details page,
// passing the device.id
data: 'device.name'
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "ENDORSEMENT_CREDENTIAL")
}
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "PLATFORM_CREDENTIAL")
}
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "FIRMWARE")
}
}
];
//Set data tables
let dataTable = setDataTables("#reportTable", url, columns);
dataTable.order([1, 'desc']).draw(); //order by createTime
});
$("#download").submit(function(e) {
let tableLength = $("#reportTable").rows;
let createTimes = "";
let deviceNames = "";
$('#reportTable tr').not('thead tr').each(function() {
createTimes += $(this).find("td").eq(1).html() + ",";
deviceNames += $(this).find("td").eq(2).html() + ",";
});
createTimes = createTimes.substring(0, createTimes.length - 1);
deviceNames = deviceNames.substring(0, deviceNames.length - 1);
let params = [
{
name: 'createTimes',
value: createTimes
},
{
name: 'deviceNames',
value: deviceNames
}
];
$(this).append($.map(params, function(param) {
return $('<input>', {
type: 'hidden',
name: param.name,
value: param.value
});
}));
});
$(".btn-primary").click(function() {
$("#validationReportsDownload").modal('hide');
});
/**
* Gets HTML to display (icon tag) for the specified validation type.
* If a validation for the requested type is not found, an empty
* string is returned (and no icon will be displayed).
*/
function getValidationDisplayHtml(full, validation_type) {
let html = '';
// loop through all the validations, looking for the one matching
// the validation_type.
for (let i = 0; i < full.validations.length; i++) {
let curValidation = full.validations[i];
let curResult = curValidation.validationResult;
let curMessage = curValidation.message;
if (curValidation.validationType === validation_type) {
let unknownStatus = '<img class="icon" src="${unknownIcon}" title="${unknownText}"/>';
// display appropriate icon based on result
if (curResult) {
// if this validation is associated with a certificate,
// link to the details page
if (curValidation.certificatesUsed.length > 0) {
let certType = '';
switch (validation_type) {
case "PLATFORM_CREDENTIAL":
case "PLATFORM_CREDENTIAL_ATTRIBUTES":
certType = "platform";
break;
case "ENDORSEMENT_CREDENTIAL":
certType = "endorsement";
break;
}
switch (validation_type) {
case "PLATFORM_CREDENTIAL":
case "PLATFORM_CREDENTIAL_ATTRIBUTES":
case "ENDORSEMENT_CREDENTIAL":
html += '<a href="${portal}/certificate-details?id='
+ curValidation.certificatesUsed[0].id
+ '&type=' + certType + '">';
break;
}
}
switch (validation_type) {
case "FIRMWARE":
html += '<a href="${portal}/rim-details?id='
+ curValidation.rimId + '">';
break;
}
switch (curResult) {
case "PASS":
html += '<img src="${passIcon}" title="' + curMessage + '"/>';
break;
case "FAIL":
html += '<img src="${failIcon}" title="' + curMessage + '"/>';
break;
case "ERROR":
html += '<img src="${errorIcon}" title="' + curMessage + '"/>';
break;
default:
html += unknownStatus;
break;
}
// add closing tag for href tag if needed.
if (curValidation.certificatesUsed.length > 0 || curValidation.rimId !== "") {
html += '</a>';
}
} else {
html += unknownStatus;
}
}
// create status icon
let result = full.overallValidationResult;
let overallMessage = full.message;
if (result) {
switch (result) {
case "PASS":
html += '<img src="${passIcon}" title="${passText}"/>';
break;
case "FAIL":
html +=
'<img src="${failIcon}" title="' + overallMessage + '"/>';
break;
case "ERROR":
html +=
'<img src="${errorIcon}" title="' +
overallMessage +
'"/>';
break;
default:
html += unknownStatus;
break;
}
return html;
}
</script>
</jsp:body>
} else {
html += unknownStatus;
}
</my:page>
return html;
},
},
{
// Note: DB column is create_time, while the
// JSON property / java property is createTime. Need to sort
// on the field createTime, but the column's
// date source is create_time.
name: "create_time",
data: "create_time",
name: "createTime",
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return formatCertificateDate(full.createTime);
},
},
{
// TODO render a link to a device details page,
// passing the device.id
name: "device.name",
data: "device.name",
searchable: true,
orderable: true
},
{
data: "id",
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "ENDORSEMENT_CREDENTIAL");
},
},
{
data: "id",
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "PLATFORM_CREDENTIAL");
},
},
{
data: "id",
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "FIRMWARE");
},
},
];
//Set data tables
let dataTable = setDataTables("#reportTable", url, columns);
dataTable.order([1, "desc"]).draw(); //order by createTime
});
$("#download").submit(function (e) {
let tableLength = $("#reportTable").rows;
let createTimes = "";
let deviceNames = "";
$("#reportTable tr")
.not("thead tr")
.each(function () {
createTimes += $(this).find("td").eq(1).html() + ",";
deviceNames += $(this).find("td").eq(2).html() + ",";
});
createTimes = createTimes.substring(0, createTimes.length - 1);
deviceNames = deviceNames.substring(0, deviceNames.length - 1);
let params = [
{
name: "createTimes",
value: createTimes,
},
{
name: "deviceNames",
value: deviceNames,
},
];
$(this).append(
$.map(params, function (param) {
return $("<input>", {
type: "hidden",
name: param.name,
value: param.value,
});
})
);
});
$(".btn-primary").click(function () {
$("#validationReportsDownload").modal("hide");
});
/**
* Gets HTML to display (icon tag) for the specified validation type.
* If a validation for the requested type is not found, an empty
* string is returned (and no icon will be displayed).
*/
function getValidationDisplayHtml(full, validation_type) {
let html = "";
// loop through all the validations, looking for the one matching
// the validation_type.
for (let i = 0; i < full.validations.length; i++) {
let curValidation = full.validations[i];
let curResult = curValidation.validationResult;
let curMessage = curValidation.message;
if (curValidation.validationType === validation_type) {
let unknownStatus =
'<img class="icon" src="${unknownIcon}" title="${unknownText}"/>';
// display appropriate icon based on result
if (curResult) {
// if this validation is associated with a certificate,
// link to the details page
if (curValidation.certificatesUsed.length > 0) {
let certType = "";
switch (validation_type) {
case "PLATFORM_CREDENTIAL":
case "PLATFORM_CREDENTIAL_ATTRIBUTES":
certType = "platform";
break;
case "ENDORSEMENT_CREDENTIAL":
certType = "endorsement";
break;
}
switch (validation_type) {
case "PLATFORM_CREDENTIAL":
case "PLATFORM_CREDENTIAL_ATTRIBUTES":
case "ENDORSEMENT_CREDENTIAL":
html +=
'<a href="${portal}/certificate-details?id=' +
curValidation.certificatesUsed[0].id +
"&type=" +
certType +
'">';
break;
}
}
switch (validation_type) {
case "FIRMWARE":
html +=
'<a href="${portal}/rim-details?id=' +
curValidation.rimId +
'">';
break;
}
switch (curResult) {
case "PASS":
html += '<img src="${passIcon}" title="' + curMessage + '"/>';
break;
case "FAIL":
html += '<img src="${failIcon}" title="' + curMessage + '"/>';
break;
case "ERROR":
html +=
'<img src="${errorIcon}" title="' + curMessage + '"/>';
break;
default:
html += unknownStatus;
break;
}
// add closing tag for href tag if needed.
if (
curValidation.certificatesUsed.length > 0 ||
curValidation.rimId !== ""
) {
html += "</a>";
}
} else {
html += unknownStatus;
}
}
}
return html;
}
</script>
</jsp:body>
</my:page>