diff --git a/src/integration-test/java/org/openlmis/report/repository/BaseCrudRepositoryIntegrationTest.java b/src/integration-test/java/org/openlmis/report/repository/BaseCrudRepositoryIntegrationTest.java index b64df60..fafe448 100644 --- a/src/integration-test/java/org/openlmis/report/repository/BaseCrudRepositoryIntegrationTest.java +++ b/src/integration-test/java/org/openlmis/report/repository/BaseCrudRepositoryIntegrationTest.java @@ -24,12 +24,14 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.repository.CrudRepository; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @Transactional @SpringBootTest @DirtiesContext +@ActiveProfiles("test") @RunWith(SpringRunner.class) public abstract class BaseCrudRepositoryIntegrationTest { @@ -41,12 +43,17 @@ public abstract class BaseCrudRepositoryIntegrationTest { */ abstract T generateInstance(); - private AtomicInteger instanceNumber = new AtomicInteger(0); + private final AtomicInteger instanceNumber = new AtomicInteger(0); + private final AtomicInteger categoryNumber = new AtomicInteger(0); int getNextInstanceNumber() { return this.instanceNumber.incrementAndGet(); } + int getNextCategoryNumber() { + return this.categoryNumber.incrementAndGet(); + } + protected void assertInstance(T instance) { Assert.assertNotNull(instance.getId()); } diff --git a/src/integration-test/java/org/openlmis/report/repository/DashboardReportRepositoryIntegrationTest.java b/src/integration-test/java/org/openlmis/report/repository/DashboardReportRepositoryIntegrationTest.java new file mode 100644 index 0000000..19443f5 --- /dev/null +++ b/src/integration-test/java/org/openlmis/report/repository/DashboardReportRepositoryIntegrationTest.java @@ -0,0 +1,222 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.repository; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.openlmis.report.domain.DashboardReport; +import org.openlmis.report.domain.ReportCategory; +import org.openlmis.report.utils.DashboardReportDataBuilder; +import org.openlmis.report.utils.ReportCategoryDataBuilder; +import org.openlmis.report.utils.ReportType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +public class DashboardReportRepositoryIntegrationTest extends + BaseCrudRepositoryIntegrationTest { + private static final String NAME = "DashboardReportIntegrationTest"; + private static final String UPDATED_NAME = "UPDATED_DashboardReportIntegrationTest"; + private static final String URL = "http://example.com"; + private static final String CATEGORY_NAME = "Default Category"; + + @Autowired + private DashboardReportRepository dashboardReportRepository; + + @Autowired + private ReportCategoryRepository reportCategoryRepository; + + @Override + DashboardReportRepository getRepository() { + return this.dashboardReportRepository; + } + + @Override + protected DashboardReport generateInstance() { + ReportCategory category = reportCategoryRepository + .save(new ReportCategoryDataBuilder().buildAsNew()); + + return new DashboardReportDataBuilder() + .withName(NAME) + .withUrl(URL) + .withType(ReportType.POWERBI) + .withEnabled(true) + .withShowOnHomePage(false) + .withCategory(category) + .buildAsNew(); + } + + @BeforeEach + public void setUp() { + dashboardReportRepository.deleteAll(); + reportCategoryRepository.deleteAll(); + } + + @Test + public void shouldFindDashboardReportByName() { + DashboardReport dashboardReport = dashboardReportRepository.save(generateInstance()); + boolean exists = dashboardReportRepository.existsByName(dashboardReport.getName()); + + assertThat(exists, is(true)); + } + + @Test + public void shouldFindAllDashboardReports() { + Pageable pageable = PageRequest.of(0, 3); + ReportCategory defaultCategory = reportCategoryRepository.save( + new ReportCategoryDataBuilder().buildAsNew() + ); + + DashboardReport dashboardReport1 = new DashboardReportDataBuilder() + .withCategory(defaultCategory) + .withShowOnHomePage(true) + .build(); + DashboardReport dashboardReport2 = new DashboardReportDataBuilder() + .withCategory(defaultCategory) + .build(); + DashboardReport dashboardReport3 = new DashboardReportDataBuilder() + .withCategory(defaultCategory) + .build(); + + dashboardReportRepository.save(dashboardReport1); + dashboardReportRepository.save(dashboardReport2); + dashboardReportRepository.save(dashboardReport3); + + Page page = dashboardReportRepository.findAll(pageable); + List found = page.getContent(); + + assertThat(found.size(), is(3)); + assertThat(page.getTotalElements(), is(3L)); + } + + @Test + public void shouldDeleteDashboardReportAndLeaveCategory() { + ReportCategory defaultCategory = reportCategoryRepository.save( + new ReportCategoryDataBuilder().buildAsNew() + ); + + DashboardReport dashboardReport = new DashboardReportDataBuilder() + .withCategory(defaultCategory) + .build(); + + dashboardReportRepository.save(dashboardReport); + dashboardReportRepository.delete(dashboardReport); + + boolean dashboardReportExists = dashboardReportRepository.existsById(dashboardReport.getId()); + assertThat(dashboardReportExists, is(false)); + + boolean reportCategoryExists = reportCategoryRepository.existsById(defaultCategory.getId()); + assertThat(reportCategoryExists, is(true)); + } + + @Test + public void shouldUpdateDashboardReport() { + ReportCategory reportCategory = reportCategoryRepository.save( + new ReportCategoryDataBuilder().buildAsNew() + ); + DashboardReport dashboardReport = dashboardReportRepository.save( + new DashboardReportDataBuilder().withName(NAME).withCategory(reportCategory).build() + ); + + assertThat(dashboardReport.getName(), is(NAME)); + assertThat(dashboardReport.getType(), is(ReportType.SUPERSET)); + assertThat(dashboardReport.getCategory().getId(), is(reportCategory.getId())); + + ReportCategory newReportCategory = reportCategoryRepository.save( + new ReportCategoryDataBuilder().withName(CATEGORY_NAME).build() + ); + + dashboardReport.setName(UPDATED_NAME); + dashboardReport.setType(ReportType.POWERBI); + dashboardReport.setCategory(newReportCategory); + + dashboardReportRepository.save(dashboardReport); + + DashboardReport updatedReport = dashboardReportRepository.findById( + dashboardReport.getId()).orElse(null); + + assertThat(updatedReport, is(notNullValue())); + assertThat(updatedReport.getName(), is(UPDATED_NAME)); + assertThat(updatedReport.getType(), is(ReportType.POWERBI)); + assertThat(updatedReport.getCategory().getId(), is(newReportCategory.getId())); + } + + @Test + public void shouldSaveDashboardReport() { + ReportCategory reportCategory = reportCategoryRepository.save( + new ReportCategoryDataBuilder().buildAsNew() + ); + DashboardReport dashboardReport = new DashboardReportDataBuilder().withName(NAME) + .withCategory(reportCategory).build(); + DashboardReport savedReport = dashboardReportRepository.save(dashboardReport); + + assertThat(savedReport.getId(), is(notNullValue())); + assertThat(savedReport.getName(), is(NAME)); + } + + @Test + public void shouldNotFindNonExistentDashboardReportById() { + Optional foundReport = dashboardReportRepository.findById(UUID.randomUUID()); + assertThat(foundReport.isPresent(), is(false)); + } + + @Test + public void shouldNotFindNonExistentDashboardReportByName() { + boolean exists = dashboardReportRepository + .existsByName("Non-Existent name"); + + assertThat(exists, is(false)); + } + + @Test + public void shouldFindOnlyEnabledDashboardReports() { + Pageable pageable = PageRequest.of(0, 10); + ReportCategory defaultCategory = reportCategoryRepository.save( + new ReportCategoryDataBuilder().buildAsNew() + ); + + DashboardReport dashboardReport1 = new DashboardReportDataBuilder() + .withCategory(defaultCategory) + .withEnabled(false) + .build(); + DashboardReport dashboardReport2 = new DashboardReportDataBuilder() + .withCategory(defaultCategory) + .withEnabled(false) + .build(); + DashboardReport dashboardReport3 = new DashboardReportDataBuilder() + .withCategory(defaultCategory) + .build(); + + dashboardReportRepository.save(dashboardReport1); + dashboardReportRepository.save(dashboardReport2); + dashboardReportRepository.save(dashboardReport3); + + Page page = dashboardReportRepository.findByEnabled(true, pageable); + List found = page.getContent(); + + assertThat(found.size(), is(1)); + assertThat(page.getTotalElements(), is(1L)); + } +} diff --git a/src/integration-test/java/org/openlmis/report/repository/JasperTemplateRepositoryIntegrationTest.java b/src/integration-test/java/org/openlmis/report/repository/JasperTemplateRepositoryIntegrationTest.java index bb89de7..67462be 100644 --- a/src/integration-test/java/org/openlmis/report/repository/JasperTemplateRepositoryIntegrationTest.java +++ b/src/integration-test/java/org/openlmis/report/repository/JasperTemplateRepositoryIntegrationTest.java @@ -22,19 +22,26 @@ import static org.junit.Assert.assertThat; import java.util.Collections; + import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; import org.openlmis.report.domain.JasperTemplate; import org.openlmis.report.domain.JasperTemplateParameter; +import org.openlmis.report.domain.ReportCategory; import org.springframework.beans.factory.annotation.Autowired; public class JasperTemplateRepositoryIntegrationTest extends BaseCrudRepositoryIntegrationTest { private static final String NAME = "TemplateRepositoryIntegrationTest"; + private static final String CATEGORY_NAME = "Default Category"; @Autowired private JasperTemplateRepository jasperTemplateRepository; + @Autowired + private ReportCategoryRepository reportCategoryRepository; + @Override JasperTemplateRepository getRepository() { return this.jasperTemplateRepository; @@ -42,11 +49,23 @@ JasperTemplateRepository getRepository() { @Override protected JasperTemplate generateInstance() { + ReportCategory category = new ReportCategory(); + category.setName(CATEGORY_NAME + getNextCategoryNumber()); + ReportCategory savedCategory = reportCategoryRepository.save(category); + JasperTemplate jasperTemplate = new JasperTemplate(); jasperTemplate.setName(NAME + getNextInstanceNumber()); + jasperTemplate.setCategory(savedCategory); + return jasperTemplate; } + @BeforeEach + public void setUp() { + jasperTemplateRepository.deleteAll(); + reportCategoryRepository.deleteAll(); + } + @Test public void shouldFindTemplateByName() { // given diff --git a/src/integration-test/java/org/openlmis/report/repository/ReportCategoryRepositoryIntegrationTest.java b/src/integration-test/java/org/openlmis/report/repository/ReportCategoryRepositoryIntegrationTest.java new file mode 100644 index 0000000..5a7dd52 --- /dev/null +++ b/src/integration-test/java/org/openlmis/report/repository/ReportCategoryRepositoryIntegrationTest.java @@ -0,0 +1,202 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.repository; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.openlmis.report.domain.JasperTemplate; +import org.openlmis.report.domain.ReportCategory; +import org.openlmis.report.utils.DashboardReportDataBuilder; +import org.openlmis.report.utils.ReportCategoryDataBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +@SuppressWarnings("PMD.TooManyMethods") +public class ReportCategoryRepositoryIntegrationTest extends + BaseCrudRepositoryIntegrationTest { + private static final String NAME = "ReportCategoryIntegrationTest"; + + @Autowired + private ReportCategoryRepository reportCategoryRepository; + + @Autowired + private JasperTemplateRepository jasperTemplateRepository; + + @Autowired + private DashboardReportRepository dashboardReportRepository; + + @Override + ReportCategoryRepository getRepository() { + return this.reportCategoryRepository; + } + + @Override + protected ReportCategory generateInstance() { + return new ReportCategoryDataBuilder().withName(NAME).buildAsNew(); + } + + @BeforeEach + public void setUp() { + dashboardReportRepository.deleteAll(); + reportCategoryRepository.deleteAll(); + } + + @Test + public void shouldFindReportCategoryByName() { + reportCategoryRepository.save(generateInstance()); + Optional foundCategory = reportCategoryRepository.findByName(NAME); + + assertThat(foundCategory.isPresent(), is(true)); + assertThat(foundCategory.get().getName(), is(NAME)); + } + + @Test + public void shouldFindAllReportCategories() { + // Clear all the data + jasperTemplateRepository.deleteAll(); + dashboardReportRepository.deleteAll(); + reportCategoryRepository.deleteAll(); + + ReportCategory reportCategory1 = reportCategoryRepository.save( + new ReportCategoryDataBuilder().buildAsNew() + ); + ReportCategory reportCategory2 = reportCategoryRepository.save( + new ReportCategoryDataBuilder().buildAsNew() + ); + + reportCategoryRepository.save(reportCategory1); + reportCategoryRepository.save(reportCategory2); + + Pageable pageable = PageRequest.of(0, 2); + Page page = reportCategoryRepository.findAll(pageable); + List found = page.getContent(); + + assertThat(found.size(), is(2)); + assertThat(page.getTotalElements(), is(2L)); + } + + @Test + public void shouldDeleteReportCategory() { + ReportCategory reportCategory = reportCategoryRepository.save( + new ReportCategoryDataBuilder().buildAsNew() + ); + + reportCategoryRepository.deleteById(reportCategory.getId()); + + Optional foundCategory = reportCategoryRepository.findById( + reportCategory.getId()); + + assertThat(foundCategory, is(Optional.empty())); + } + + @Test + public void shouldUpdateReportCategory() { + ReportCategory reportCategory = reportCategoryRepository.save( + new ReportCategoryDataBuilder().buildAsNew() + ); + reportCategory.setName("UpdatedCategoryName"); + reportCategoryRepository.save(reportCategory); + + Optional updatedCategory = reportCategoryRepository.findById( + reportCategory.getId()); + + assertThat(updatedCategory.isPresent(), is(true)); + assertThat(updatedCategory.get().getName(), is("UpdatedCategoryName")); + } + + @Test + public void shouldSaveReportCategory() { + reportCategoryRepository.save(generateInstance()); + + Optional foundCategory = reportCategoryRepository.findByName(NAME); + + assertThat(foundCategory.isPresent(), is(true)); + assertThat(foundCategory.get().getName(), is(NAME)); + } + + @Test + public void shouldNotFindNonExistentReportCategoryById() { + Optional foundCategory = reportCategoryRepository.findById(UUID.randomUUID()); + + assertThat(foundCategory.isPresent(), is(false)); + } + + @Test + public void shouldNotFindNonExistentReportCategoryByName() { + Optional foundCategory = reportCategoryRepository + .findByName("NonExistentCategory"); + + assertThat(foundCategory.isPresent(), is(false)); + } + + @Test + public void shouldReturnTrueWhenCategoryIsAssignedToDashboardReport() { + ReportCategory reportCategory = reportCategoryRepository.save( + new ReportCategoryDataBuilder().buildAsNew() + ); + dashboardReportRepository.save( + new DashboardReportDataBuilder().withCategory(reportCategory).build() + ); + + boolean exists = dashboardReportRepository.existsByCategory_Id(reportCategory.getId()); + assertThat(exists, is(true)); + } + + @Test + public void shouldReturnFalseWhenCategoryIsNotAssignedToDashboardReport() { + ReportCategory reportCategory = reportCategoryRepository.save( + new ReportCategoryDataBuilder().buildAsNew() + ); + boolean exists = dashboardReportRepository.existsByCategory_Id(reportCategory.getId()); + assertThat(exists, is(false)); + } + + @Test + public void shouldReturnTrueWhenCategoryIsAssignedToJasperTemplate() { + ReportCategory reportCategory = reportCategoryRepository.save( + new ReportCategoryDataBuilder().buildAsNew() + ); + + JasperTemplate template = new JasperTemplate(); + template.setId(UUID.randomUUID()); + template.setName("name"); + template.setRequiredRights(new ArrayList<>()); + template.setCategory(reportCategory); + + jasperTemplateRepository.save(template); + + boolean exists = jasperTemplateRepository.existsByCategory_Id(reportCategory.getId()); + assertThat(exists, is(true)); + } + + @Test + public void shouldReturnFalseWhenCategoryIsNotAssignedToJasperTemplate() { + ReportCategory reportCategory = reportCategoryRepository.save( + new ReportCategoryDataBuilder().buildAsNew() + ); + boolean exists = jasperTemplateRepository.existsByCategory_Id(reportCategory.getId()); + assertThat(exists, is(false)); + } +} \ No newline at end of file diff --git a/src/integration-test/java/org/openlmis/report/service/JasperReportsViewServiceIntegrationTest.java b/src/integration-test/java/org/openlmis/report/service/JasperReportsViewServiceIntegrationTest.java index 5352f31..f02a44f 100644 --- a/src/integration-test/java/org/openlmis/report/service/JasperReportsViewServiceIntegrationTest.java +++ b/src/integration-test/java/org/openlmis/report/service/JasperReportsViewServiceIntegrationTest.java @@ -31,10 +31,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.transaction.annotation.Transactional; @SpringBootTest @DirtiesContext +@Transactional +@ActiveProfiles("test") @RunWith(SpringRunner.class) public class JasperReportsViewServiceIntegrationTest { diff --git a/src/integration-test/java/org/openlmis/report/web/BaseWebIntegrationTest.java b/src/integration-test/java/org/openlmis/report/web/BaseWebIntegrationTest.java index 015cfd7..b1c1825 100644 --- a/src/integration-test/java/org/openlmis/report/web/BaseWebIntegrationTest.java +++ b/src/integration-test/java/org/openlmis/report/web/BaseWebIntegrationTest.java @@ -35,7 +35,9 @@ import guru.nidi.ramltester.RamlLoaders; import guru.nidi.ramltester.restassured.RestAssuredClient; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.PostConstruct; + import org.junit.Rule; import org.junit.runner.RunWith; import org.mockito.invocation.InvocationOnMock; @@ -54,9 +56,11 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@Transactional @ActiveProfiles("test") @DirtiesContext public abstract class BaseWebIntegrationTest { @@ -65,6 +69,8 @@ public abstract class BaseWebIntegrationTest { protected static final String RAML_ASSERT_MESSAGE = "HTTP request/response should match RAML definition."; + private final AtomicInteger categoryNumber = new AtomicInteger(0); + @Rule public WireMockRule wireMockRule = new WireMockRule(80); @@ -100,6 +106,10 @@ public void init() { restAssured = ramlDefinition.createRestAssured(); } + int getNextCategoryNumber() { + return this.categoryNumber.incrementAndGet(); + } + protected void mockUserAuthenticated() { UserDto user = new UserDto(); user.setId(UUID.randomUUID()); diff --git a/src/integration-test/java/org/openlmis/report/web/DashboardReportControllerIntegrationTest.java b/src/integration-test/java/org/openlmis/report/web/DashboardReportControllerIntegrationTest.java new file mode 100644 index 0000000..ca9260d --- /dev/null +++ b/src/integration-test/java/org/openlmis/report/web/DashboardReportControllerIntegrationTest.java @@ -0,0 +1,356 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.web; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import java.util.Optional; +import java.util.UUID; + +import org.junit.Before; +import org.junit.Test; +import org.openlmis.report.domain.DashboardReport; +import org.openlmis.report.domain.ReportCategory; +import org.openlmis.report.dto.DashboardReportDto; +import org.openlmis.report.dto.external.referencedata.RightDto; +import org.openlmis.report.dto.external.referencedata.RightType; +import org.openlmis.report.repository.DashboardReportRepository; +import org.openlmis.report.repository.ReportCategoryRepository; +import org.openlmis.report.service.referencedata.RightReferenceDataService; +import org.openlmis.report.utils.ReportType; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +public class DashboardReportControllerIntegrationTest + extends BaseWebIntegrationTest { + private static final String RESOURCE_URL = "/api/reports/dashboardReports"; + private static final String ID_URL = RESOURCE_URL + "/{id}"; + private static final String RIGHT = "_RIGHT"; + + @MockBean + private DashboardReportRepository dashboardReportRepository; + + @MockBean + private ReportCategoryRepository reportCategoryRepository; + + @MockBean + private RightReferenceDataService rightReferenceDataService; + + private UUID reportId; + private DashboardReport dashboardReport; + private ReportCategory reportCategory; + private RightDto requiredRight; + + @Before + public void setUp() { + reportCategory = new ReportCategory(); + reportCategory.setId(UUID.randomUUID()); + reportCategory.setName("Sample Category"); + + dashboardReport = new DashboardReport(); + reportId = UUID.randomUUID(); + dashboardReport.setId(reportId); + dashboardReport.setName("Sample Report"); + dashboardReport.setUrl("http://example.com"); + dashboardReport.setType(ReportType.POWERBI); + dashboardReport.setEnabled(true); + dashboardReport.setShowOnHomePage(true); + dashboardReport.setCategory(reportCategory); + dashboardReport.setRightName((dashboardReport.getName() + RIGHT).toUpperCase()); + + requiredRight = new RightDto(); + requiredRight.setId(UUID.randomUUID()); + requiredRight.setName(dashboardReport.getRightName()); + requiredRight.setType(RightType.REPORTS); + } + + @Test + public void shouldReturnDashboardReportWhenReportExists() { + // Given + when(dashboardReportRepository.findById(reportId)).thenReturn(Optional.of(dashboardReport)); + + // When & Then + DashboardReportDto response = restAssured.given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .header(HttpHeaders.AUTHORIZATION, getTokenHeader()) + .when() + .get(ID_URL, reportId) + .then() + .statusCode(HttpStatus.OK.value()) + .extract().as(DashboardReportDto.class); + + // Verify + assertNotNull(response); + assertNotNull(response.getCategory()); + assertEquals(response.getId(), dashboardReport.getId()); + assertEquals(response.getUrl(), dashboardReport.getUrl()); + assertEquals(response.getType(), dashboardReport.getType()); + assertEquals(response.isEnabled(), dashboardReport.isEnabled()); + assertEquals(response.isShowOnHomePage(), dashboardReport.isShowOnHomePage()); + assertEquals(response.getCategory().getId(), reportCategory.getId()); + assertEquals(response.getCategory().getName(), reportCategory.getName()); + + verify(dashboardReportRepository).findById(reportId); + } + + @Test + public void shouldReturn404WhenReportDoesNotExist() { + // Given + when(dashboardReportRepository.findById(reportId)).thenReturn(Optional.empty()); + + // When & Then + restAssured.given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .header(HttpHeaders.AUTHORIZATION, getTokenHeader()) + .when() + .get(ID_URL, reportId) + .then() + .statusCode(HttpStatus.NOT_FOUND.value()); + } + + @Test + public void shouldReturn401WhenUnauthorized() { + // When & Then + restAssured.given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when() + .get(ID_URL, reportId) + .then() + .statusCode(HttpStatus.UNAUTHORIZED.value()); + } + + @Test + public void shouldDeleteDashboardReport() { + // Given + when(dashboardReportRepository.findById(reportId)).thenReturn(Optional.of(dashboardReport)); + when(rightReferenceDataService.findRight(dashboardReport.getRightName())) + .thenReturn(requiredRight); + doNothing().when(dashboardReportRepository).delete(dashboardReport); + doNothing().when(rightReferenceDataService).delete(requiredRight.getId()); + + // When & Then + restAssured.given() + .header(HttpHeaders.AUTHORIZATION, getTokenHeader()) + .when() + .delete(ID_URL, reportId) + .then() + .statusCode(HttpStatus.NO_CONTENT.value()); + + // Verify + verify(dashboardReportRepository).findById(reportId); + verify(dashboardReportRepository).delete(dashboardReport); + verify(rightReferenceDataService).findRight(requiredRight.getName()); + verify(rightReferenceDataService).delete(requiredRight.getId()); + } + + @Test + public void shouldThrowExceptionWhenDeletingNonExistentDashboardReport() { + // Given + UUID reportId = UUID.randomUUID(); + when(dashboardReportRepository.findById(reportId)).thenReturn(Optional.empty()); + + // When & Then + restAssured.given() + .header(HttpHeaders.AUTHORIZATION, getTokenHeader()) + .when() + .delete(ID_URL, reportId) + .then() + .statusCode(HttpStatus.NOT_FOUND.value()); + + // Verify + verify(dashboardReportRepository).findById(reportId); + verify(dashboardReportRepository, never()).delete(any()); + } + + @Test + public void shouldCreateDashboardReport() throws Exception { + // Given + when(reportCategoryRepository.findById(any(UUID.class))).thenReturn( + Optional.ofNullable(reportCategory)); + doNothing().when(rightReferenceDataService).save(any(RightDto.class)); + + DashboardReportDto dto = new DashboardReportDto(); + dto.setName("Unique Report Name"); + dto.setUrl("http://url.com"); + dto.setType(ReportType.POWERBI); + dto.setEnabled(true); + dto.setShowOnHomePage(true); + dto.setCategory(reportCategory); + dto.setRightName((dto.getName() + RIGHT).toUpperCase()); + + DashboardReport createdReport = new DashboardReport(); + createdReport.updateFrom(dto); + + when(dashboardReportRepository.existsByName(dto.getName())).thenReturn(false); + when(dashboardReportRepository.save(any(DashboardReport.class))).thenReturn(createdReport); + + // When & Then + DashboardReportDto response = restAssured.given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .header(HttpHeaders.AUTHORIZATION, getTokenHeader()) + .body(dto) + .when() + .post(RESOURCE_URL) + .then() + .statusCode(HttpStatus.OK.value()) + .extract().as(DashboardReportDto.class); + + // Verify + assertNotNull(response); + assertNotNull(response.getCategory()); + assertEquals(dto.getName(), response.getName()); + assertEquals(dto.getUrl(), response.getUrl()); + assertEquals(dto.getType(), response.getType()); + assertEquals(dto.isShowOnHomePage(), response.isShowOnHomePage()); + assertEquals(dto.isEnabled(), response.isEnabled()); + assertEquals(dto.getCategory().getId(), response.getCategory().getId()); + assertEquals(dto.getCategory().getName(), response.getCategory().getName()); + + verify(reportCategoryRepository).findById(reportCategory.getId()); + verify(dashboardReportRepository).existsByName(dto.getName()); + verify(dashboardReportRepository).save(any(DashboardReport.class)); + verify(rightReferenceDataService).save(any(RightDto.class)); + } + + @Test + public void shouldThrowExceptionWhenCreatingDashboardReportWithDuplicateName() { + // Given + DashboardReportDto dto = new DashboardReportDto(); + dto.setName("Duplicate Report Name"); + dto.setUrl("http://url.com"); + dto.setType(ReportType.POWERBI); + dto.setEnabled(true); + dto.setShowOnHomePage(true); + dto.setCategory(reportCategory); + dto.setRightName((dto.getName() + RIGHT).toUpperCase()); + + when(dashboardReportRepository.existsByName(dto.getName())).thenReturn(true); + + // When & Then + restAssured.given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .header(HttpHeaders.AUTHORIZATION, getTokenHeader()) + .body(dto) + .when() + .post(RESOURCE_URL) + .then() + .statusCode(HttpStatus.BAD_REQUEST.value()); + + // Verify + verify(dashboardReportRepository).existsByName(dto.getName()); + verify(dashboardReportRepository, never()).save(any(DashboardReport.class)); + } + + @Test + public void shouldUpdateDashboardReport() { + // Given + DashboardReportDto updatedDto = new DashboardReportDto(); + updatedDto.setId(reportId); + updatedDto.setName("Updated Report Name"); + updatedDto.setUrl("http://new-url.com"); + updatedDto.setEnabled(false); + updatedDto.setShowOnHomePage(true); + updatedDto.setCategory(reportCategory); + + when(dashboardReportRepository.findById(reportId)).thenReturn(Optional.of(dashboardReport)); + when(reportCategoryRepository.findById(any(UUID.class))).thenReturn( + Optional.ofNullable(reportCategory)); + + // When & Then + DashboardReportDto response = restAssured.given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .header(HttpHeaders.AUTHORIZATION, getTokenHeader()) + .body(updatedDto) + .when() + .put(ID_URL, reportId) + .then() + .statusCode(HttpStatus.OK.value()) + .extract().as(DashboardReportDto.class); + + // Verify + assertNotNull(response); + assertNotNull(response.getCategory()); + assertEquals(updatedDto.getName(), response.getName()); + assertEquals(updatedDto.getUrl(), response.getUrl()); + assertEquals(updatedDto.isEnabled(), response.isEnabled()); + assertEquals(updatedDto.isShowOnHomePage(), response.isShowOnHomePage()); + assertEquals(updatedDto.getCategory().getId(), response.getCategory().getId()); + assertEquals(updatedDto.getCategory().getName(), response.getCategory().getName()); + + verify(dashboardReportRepository).findById(reportId); + verify(dashboardReportRepository).save(any(DashboardReport.class)); + } + + @Test + public void shouldThrowExceptionWhenReportIdMismatchDuringUpdate() { + // Given + UUID mismatchedId = UUID.randomUUID(); + DashboardReportDto updatedDto = new DashboardReportDto(); + updatedDto.setId(mismatchedId); + updatedDto.setName("Updated Report Name"); + updatedDto.setUrl("http://new-url.com"); + updatedDto.setEnabled(false); + updatedDto.setShowOnHomePage(true); + updatedDto.setCategory(reportCategory); + + // When & Then + restAssured.given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .header(HttpHeaders.AUTHORIZATION, getTokenHeader()) + .body(updatedDto) + .when() + .put(ID_URL, reportId) + .then() + .statusCode(HttpStatus.BAD_REQUEST.value()); + + // Verify + verifyNoInteractions(dashboardReportRepository); + } + + @Test + public void shouldThrowNotFoundExceptionWhenReportDoesNotExist() { + // Given + UUID reportId = UUID.randomUUID(); + dashboardReport.setId(reportId); + + when(dashboardReportRepository.findById(reportId)).thenReturn(Optional.empty()); + + // When & Then + restAssured.given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .header(HttpHeaders.AUTHORIZATION, getTokenHeader()) + .body(dashboardReport) + .when() + .put(ID_URL, reportId) + .then() + .statusCode(HttpStatus.NOT_FOUND.value()); + + // Verify + verify(dashboardReportRepository).findById(reportId); + verifyNoMoreInteractions(dashboardReportRepository); + } + +} diff --git a/src/integration-test/java/org/openlmis/report/web/JasperTemplateControllerIntegrationTest.java b/src/integration-test/java/org/openlmis/report/web/JasperTemplateControllerIntegrationTest.java index 93448e5..85b46c9 100644 --- a/src/integration-test/java/org/openlmis/report/web/JasperTemplateControllerIntegrationTest.java +++ b/src/integration-test/java/org/openlmis/report/web/JasperTemplateControllerIntegrationTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; @@ -32,12 +33,15 @@ import java.util.Collections; import java.util.Optional; import java.util.UUID; + import org.junit.Before; import org.junit.Test; import org.openlmis.report.domain.JasperTemplate; +import org.openlmis.report.domain.ReportCategory; import org.openlmis.report.dto.JasperTemplateDto; import org.openlmis.report.exception.PermissionMessageException; import org.openlmis.report.repository.JasperTemplateRepository; +import org.openlmis.report.repository.ReportCategoryRepository; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -50,13 +54,27 @@ public class JasperTemplateControllerIntegrationTest extends BaseWebIntegrationT private static final String FORMAT_PARAM = "format"; private static final String REPORT_URL = ID_URL + "/{" + FORMAT_PARAM + "}"; private static final String PDF_FORMAT = "pdf"; + private static final String CATEGORY_NAME = "Default Category"; @MockBean private JasperTemplateRepository jasperTemplateRepository; + @MockBean + private ReportCategoryRepository reportCategoryRepository; + + private ReportCategory reportCategory; + @Before public void setUp() { mockUserAuthenticated(); + + reportCategory = new ReportCategory(); + reportCategory.setId(UUID.randomUUID()); + reportCategory.setName(CATEGORY_NAME); + + given(reportCategoryRepository.findByName(anyString())).willReturn( + Optional.ofNullable(reportCategory)); + given(reportCategoryRepository.save(any(ReportCategory.class))).willReturn(reportCategory); } // GET /api/reports/templates @@ -244,10 +262,10 @@ private JasperTemplate generateExistentTemplate() { private JasperTemplate generateExistentTemplate(UUID id) { JasperTemplate template = new JasperTemplate(); - template.setId(id); template.setName("name"); template.setRequiredRights(new ArrayList<>()); + template.setCategory(reportCategory); given(jasperTemplateRepository.findById(id)).willReturn(Optional.of(template)); diff --git a/src/integration-test/java/org/openlmis/report/web/ReportCategoryControllerIntegrationTest.java b/src/integration-test/java/org/openlmis/report/web/ReportCategoryControllerIntegrationTest.java new file mode 100644 index 0000000..5e6a18c --- /dev/null +++ b/src/integration-test/java/org/openlmis/report/web/ReportCategoryControllerIntegrationTest.java @@ -0,0 +1,118 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.web; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Optional; +import java.util.UUID; + +import org.junit.Before; +import org.junit.Test; +import org.openlmis.report.domain.ReportCategory; +import org.openlmis.report.dto.ReportCategoryDto; +import org.openlmis.report.repository.ReportCategoryRepository; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +public class ReportCategoryControllerIntegrationTest + extends BaseWebIntegrationTest { + private static final String RESOURCE_URL = "/api/reports/reportCategories"; + private static final String ID_URL = RESOURCE_URL + "/{id}"; + + @MockBean + private ReportCategoryRepository reportCategoryRepository; + + private UUID reportCategoryId; + private ReportCategory reportCategory; + + @Before + public void setUp() { + reportCategory = new ReportCategory(); + reportCategoryId = UUID.randomUUID(); + reportCategory.setId(reportCategoryId); + reportCategory.setName("Test Category"); + } + + @Test + public void shouldReturnReportCategoryWhenCategoryExists() { + when(reportCategoryRepository.findById(reportCategoryId)).thenReturn( + Optional.of(reportCategory)); + + // When & Then + ReportCategoryDto response = restAssured.given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .header(HttpHeaders.AUTHORIZATION, getTokenHeader()) + .when() + .get(ID_URL, reportCategoryId) + .then() + .statusCode(HttpStatus.OK.value()) + .extract().as(ReportCategoryDto.class); + + // Verify + assertNotNull(response); + assertEquals(response.getId(), reportCategory.getId()); + assertEquals(response.getName(), reportCategory.getName()); + + verify(reportCategoryRepository).findById(reportCategoryId); + } + + @Test + public void shouldUpdateReportCategoryWhenValidDataProvided() { + // Given + ReportCategoryDto updatedDto = new ReportCategoryDto(); + updatedDto.setId(reportCategoryId); + updatedDto.setName("Updated Category Name"); + + ReportCategory updatedCategory = new ReportCategory(); + updatedCategory.setId(reportCategoryId); + updatedCategory.setName(updatedDto.getName()); + + when(reportCategoryRepository.existsByIdIsNotAndName( + reportCategoryId, updatedDto.getName())).thenReturn(false); + when(reportCategoryRepository.findById(reportCategoryId)).thenReturn( + Optional.of(reportCategory)); + when(reportCategoryRepository.save(any(ReportCategory.class))).thenReturn(updatedCategory); + + // When & Then + ReportCategoryDto response = restAssured.given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .header(HttpHeaders.AUTHORIZATION, getTokenHeader()) + .body(updatedDto) + .when() + .put(ID_URL, reportCategoryId) + .then() + .statusCode(HttpStatus.OK.value()) + .extract().as(ReportCategoryDto.class); + + // Verify + assertNotNull(response); + assertEquals(response.getId(), updatedDto.getId()); + assertEquals(response.getName(), updatedDto.getName()); + + verify(reportCategoryRepository).existsByIdIsNotAndName( + reportCategoryId, updatedDto.getName()); + verify(reportCategoryRepository).findById(reportCategoryId); + verify(reportCategoryRepository).save(any(ReportCategory.class)); + } + +} diff --git a/src/main/java/org/openlmis/report/domain/BaseEntity.java b/src/main/java/org/openlmis/report/domain/BaseEntity.java index 9828d6c..e28d485 100644 --- a/src/main/java/org/openlmis/report/domain/BaseEntity.java +++ b/src/main/java/org/openlmis/report/domain/BaseEntity.java @@ -30,6 +30,7 @@ public abstract class BaseEntity { protected static final String TEXT_COLUMN_DEFINITION = "text"; protected static final String UUID_COLUMN_DEFINITION = "pg-uuid"; + protected static final String BOOLEAN_COLUMN_DEFINITION = "boolean"; @Id @GeneratedValue(generator = "uuid-gen") diff --git a/src/main/java/org/openlmis/report/domain/DashboardReport.java b/src/main/java/org/openlmis/report/domain/DashboardReport.java new file mode 100644 index 0000000..5779bb0 --- /dev/null +++ b/src/main/java/org/openlmis/report/domain/DashboardReport.java @@ -0,0 +1,145 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.domain; + +import java.util.UUID; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.openlmis.report.utils.ReportType; + +@Getter +@Setter +@Entity +@Table(name = "dashboard_reports", schema = "report") +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class DashboardReport extends BaseEntity { + @Column(columnDefinition = TEXT_COLUMN_DEFINITION, nullable = false) + private String name; + + @Column(columnDefinition = TEXT_COLUMN_DEFINITION, nullable = false) + private String url; + + @Enumerated(EnumType.STRING) + @Column(columnDefinition = TEXT_COLUMN_DEFINITION, nullable = false) + private ReportType type; + + @Column(columnDefinition = BOOLEAN_COLUMN_DEFINITION, nullable = false) + private boolean enabled; + + @Column(columnDefinition = BOOLEAN_COLUMN_DEFINITION, nullable = false) + private boolean showOnHomePage; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "categoryid", referencedColumnName = "id", nullable = false) + private ReportCategory category; + + @Column(columnDefinition = TEXT_COLUMN_DEFINITION, nullable = false) + private String rightName; + + /** + * Create new instance of a Dashboard report based on data from {@link DashboardReport.Importer}. + * + * @param importer instance of {@link DashboardReport.Importer}. + * @return new instance of a dashboard report. + */ + public static DashboardReport newInstance(DashboardReport.Importer importer) { + DashboardReport dashboardReport = new DashboardReport(); + dashboardReport.setId(importer.getId()); + dashboardReport.updateFrom(importer); + + return dashboardReport; + } + + /** + * Copy values of attributes into new or updated Dashboard Report. + * + * @param importer Dashboard report importer with new values. + */ + public void updateFrom(DashboardReport.Importer importer) { + this.name = importer.getName(); + this.url = importer.getUrl(); + this.type = importer.getType(); + this.enabled = importer.isEnabled(); + this.showOnHomePage = importer.isShowOnHomePage(); + this.category = importer.getCategory(); + } + + /** + * Export this object to the specified exporter (DTO). + * + * @param exporter exporter to export to. + */ + public void export(DashboardReport.Exporter exporter) { + exporter.setId(id); + exporter.setName(name); + exporter.setUrl(url); + exporter.setType(type); + exporter.setEnabled(enabled); + exporter.setShowOnHomePage(showOnHomePage); + exporter.setCategory(category); + exporter.setRightName(rightName); + } + + public interface Exporter { + void setId(UUID id); + + void setName(String name); + + void setUrl(String url); + + void setType(ReportType type); + + void setEnabled(boolean enabled); + + void setShowOnHomePage(boolean showOnHomePage); + + void setCategory(ReportCategory category); + + void setRightName(String name); + } + + public interface Importer { + UUID getId(); + + String getName(); + + String getUrl(); + + ReportType getType(); + + boolean isEnabled(); + + boolean isShowOnHomePage(); + + ReportCategory getCategory(); + + String getRightName(); + } +} \ No newline at end of file diff --git a/src/main/java/org/openlmis/report/domain/JasperTemplate.java b/src/main/java/org/openlmis/report/domain/JasperTemplate.java index 2d02fb8..2a37777 100644 --- a/src/main/java/org/openlmis/report/domain/JasperTemplate.java +++ b/src/main/java/org/openlmis/report/domain/JasperTemplate.java @@ -28,6 +28,7 @@ import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; @@ -40,6 +41,8 @@ import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; +@Getter +@Setter @Builder @Entity @Table(name = "jasper_templates") @@ -48,29 +51,19 @@ public class JasperTemplate extends BaseEntity { @Column(columnDefinition = TEXT_COLUMN_DEFINITION, unique = true, nullable = false) - @Getter - @Setter private String name; @Column - @Getter - @Setter private byte[] data; @Column(columnDefinition = TEXT_COLUMN_DEFINITION) - @Getter - @Setter private String type; @Column(columnDefinition = TEXT_COLUMN_DEFINITION) - @Getter - @Setter private String description; @ElementCollection @CollectionTable - @Getter - @Setter private List requiredRights; @OneToMany( @@ -79,22 +72,20 @@ public class JasperTemplate extends BaseEntity { fetch = FetchType.EAGER, orphanRemoval = true) @Fetch(FetchMode.SELECT) - @Getter - @Setter private List templateParameters; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "jasper_templates_report_images", joinColumns = @JoinColumn(name = "jaspertemplateid", nullable = false), inverseJoinColumns = @JoinColumn(name = "reportimageid", nullable = false)) - @Getter - @Setter private Set reportImages = new HashSet<>(); - @Getter - @Setter private Boolean visible; + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "categoryid", referencedColumnName = "id", nullable = false) + private ReportCategory category; + /** * Export this object to the specified exporter (DTO). * @@ -106,6 +97,7 @@ public void export(Exporter exporter) { exporter.setId(id); exporter.setName(name); exporter.setType(type); + exporter.setCategory(category); } @PrePersist @@ -132,5 +124,7 @@ public interface Exporter { void setDescription(String description); void setRequiredRights(List rights); + + void setCategory(ReportCategory category); } } diff --git a/src/main/java/org/openlmis/report/domain/ReportCategory.java b/src/main/java/org/openlmis/report/domain/ReportCategory.java new file mode 100644 index 0000000..4b77de4 --- /dev/null +++ b/src/main/java/org/openlmis/report/domain/ReportCategory.java @@ -0,0 +1,86 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.domain; + +import java.util.UUID; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "report_categories") +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class ReportCategory extends BaseEntity { + + @Column(columnDefinition = TEXT_COLUMN_DEFINITION, nullable = false, unique = true) + private String name; + + /** + * Create a new instance of a Report category based on data. + * from {@link ReportCategory.Importer}. + * + * @param importer instance of {@link ReportCategory.Importer}. + * @return new instance of a report category. + */ + public static ReportCategory newInstance(ReportCategory.Importer importer) { + ReportCategory category = new ReportCategory(); + category.setId(importer.getId()); + category.updateFrom(importer); + + return category; + } + + /** + * Copy values of attributes into new or updated Report Category. + * + * @param importer Report category importer with new values. + */ + public void updateFrom(ReportCategory.Importer importer) { + this.name = importer.getName(); + } + + /** + * Export this object to the specified exporter (DTO). + * + * @param exporter exporter to export to. + */ + public void export(ReportCategory.Exporter exporter) { + exporter.setId(id); + exporter.setName(name); + } + + public interface Exporter { + void setId(UUID id); + + void setName(String name); + } + + public interface Importer { + UUID getId(); + + String getName(); + } + +} \ No newline at end of file diff --git a/src/main/java/org/openlmis/report/dto/DashboardReportDto.java b/src/main/java/org/openlmis/report/dto/DashboardReportDto.java new file mode 100644 index 0000000..9ad0626 --- /dev/null +++ b/src/main/java/org/openlmis/report/dto/DashboardReportDto.java @@ -0,0 +1,74 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.dto; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.openlmis.report.domain.DashboardReport; +import org.openlmis.report.domain.DashboardReport.Exporter; +import org.openlmis.report.domain.DashboardReport.Importer; +import org.openlmis.report.domain.ReportCategory; + +import org.openlmis.report.utils.ReportType; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class DashboardReportDto implements Importer, Exporter { + private UUID id; + private String name; + private String url; + private ReportType type; + private boolean enabled; + private boolean showOnHomePage; + private ReportCategory category; + private String rightName; + + /** + * Create new instance of DashboardReportDto based on given {@link DashboardReport}. + * + * @param dashboardReport instance of Dashboard Report. + * @return new instance of DashboardReportDto. + */ + public static DashboardReportDto newInstance(DashboardReport dashboardReport) { + if (dashboardReport == null) { + return null; + } + DashboardReportDto dashboardReportDto = new DashboardReportDto(); + dashboardReport.export(dashboardReportDto); + return dashboardReportDto; + } + + /** + * Create new list of DashboardReportDto based on given list of {@link DashboardReport}. + * + * @param dashboardReports list of {@link DashboardReport}. + * @return new list of DashboardReportDto. + */ + public static List newInstance(Iterable dashboardReports) { + return StreamSupport.stream(dashboardReports.spliterator(), false) + .map(DashboardReportDto::newInstance) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/main/java/org/openlmis/report/dto/JasperTemplateDto.java b/src/main/java/org/openlmis/report/dto/JasperTemplateDto.java index bb49ba5..5df4d1b 100644 --- a/src/main/java/org/openlmis/report/dto/JasperTemplateDto.java +++ b/src/main/java/org/openlmis/report/dto/JasperTemplateDto.java @@ -25,38 +25,22 @@ import lombok.Setter; import org.openlmis.report.domain.JasperTemplate; import org.openlmis.report.domain.JasperTemplateParameter; +import org.openlmis.report.domain.ReportCategory; +@Getter +@Setter @AllArgsConstructor @NoArgsConstructor public class JasperTemplateDto implements JasperTemplate.Exporter { - @Getter - @Setter private UUID id; - - @Getter - @Setter private String name; - - @Getter - @Setter private String type; - - @Getter - @Setter private String description; - - @Getter - @Setter private byte[] data; - - @Getter - @Setter private List requiredRights; - - @Getter - @Setter private List templateParameters; + private ReportCategory category; /** * Create new list of JasperTemplateDto based on given list of {@link JasperTemplate}. diff --git a/src/main/java/org/openlmis/report/dto/ReportCategoryDto.java b/src/main/java/org/openlmis/report/dto/ReportCategoryDto.java new file mode 100644 index 0000000..48198c8 --- /dev/null +++ b/src/main/java/org/openlmis/report/dto/ReportCategoryDto.java @@ -0,0 +1,66 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.dto; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.openlmis.report.domain.ReportCategory; +import org.openlmis.report.domain.ReportCategory.Exporter; +import org.openlmis.report.domain.ReportCategory.Importer; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class ReportCategoryDto implements Importer, Exporter { + private UUID id; + private String name; + + /** + * Create new instance of a ReportCategoryDto based on given {@link ReportCategory}. + * + * @param reportCategory instance of a Report Category. + * @return new instance of EmbeddedReportCategoryDto. + */ + public static ReportCategoryDto newInstance(ReportCategory reportCategory) { + if (reportCategory == null) { + return null; + } + + ReportCategoryDto reportCategoryDto = new ReportCategoryDto(); + reportCategory.export(reportCategoryDto); + + return reportCategoryDto; + } + + /** + * Create new list of a ReportCategoryDto based on given list of {@link ReportCategory}. + * + * @param reportCategories list of {@link ReportCategory}. + * @return new list of ReportCategoryDto. + */ + public static List newInstance(Iterable reportCategories) { + return StreamSupport.stream(reportCategories.spliterator(), false) + .map(ReportCategoryDto::newInstance) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/main/java/org/openlmis/report/dto/external/referencedata/RightDto.java b/src/main/java/org/openlmis/report/dto/external/referencedata/RightDto.java index ed9e775..5127298 100644 --- a/src/main/java/org/openlmis/report/dto/external/referencedata/RightDto.java +++ b/src/main/java/org/openlmis/report/dto/external/referencedata/RightDto.java @@ -15,7 +15,6 @@ package org.openlmis.report.dto.external.referencedata; -import java.util.Set; import java.util.UUID; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; @@ -33,5 +32,4 @@ public final class RightDto { private String name; private RightType type; private String description; - private Set attachments; } diff --git a/src/main/java/org/openlmis/report/exception/NotFoundMessageException.java b/src/main/java/org/openlmis/report/exception/NotFoundMessageException.java index 66ce1ae..b3c3cad 100644 --- a/src/main/java/org/openlmis/report/exception/NotFoundMessageException.java +++ b/src/main/java/org/openlmis/report/exception/NotFoundMessageException.java @@ -21,6 +21,10 @@ * Exception thrown when resource was not found. */ public class NotFoundMessageException extends BaseMessageException { + public NotFoundMessageException(String message) { + super(message); + } + public NotFoundMessageException(Message message) { super(message); } diff --git a/src/main/java/org/openlmis/report/i18n/DashboardReportMessageKeys.java b/src/main/java/org/openlmis/report/i18n/DashboardReportMessageKeys.java new file mode 100644 index 0000000..f2a8306 --- /dev/null +++ b/src/main/java/org/openlmis/report/i18n/DashboardReportMessageKeys.java @@ -0,0 +1,29 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.i18n; + +public class DashboardReportMessageKeys extends MessageKeys { + public static final String ERROR_DASHBOARD_REPORT_NAME_DUPLICATED = + "report.error.dashboardReport.name.duplicated"; + public static final String ERROR_DASHBOARD_REPORT_NOT_FOUND = + "report.error.dashboardReport.notFound"; + public static final String ERROR_DASHBOARD_REPORT_ID_MISMATCH = + "report.error.dashboardReport.id.mismatch"; + public static final String ERROR_COULD_NOT_SAVE_RIGHT = + "report.error.dashboardReport.save.right.failed"; + public static final String ERROR_COULD_NOT_DELETE_RIGHT = + "report.error.dashboardReport.delete.right.failed"; +} diff --git a/src/main/java/org/openlmis/report/i18n/ReportCategoryMessageKeys.java b/src/main/java/org/openlmis/report/i18n/ReportCategoryMessageKeys.java new file mode 100644 index 0000000..da4df33 --- /dev/null +++ b/src/main/java/org/openlmis/report/i18n/ReportCategoryMessageKeys.java @@ -0,0 +1,28 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.i18n; + +public class ReportCategoryMessageKeys extends MessageKeys { + public static final String ERROR_REPORT_CATEGORY_NAME_DUPLICATED = + "report.error.reportCategory.name.duplicated"; + public static final String ERROR_REPORT_CATEGORY_NOT_FOUND = + "report.error.reportCategory.notFound"; + public static final String ERROR_REPORT_CATEGORY_ID_MISMATCH = + "report.error.reportCategory.id.mismatch"; + public static final String ERROR_CATEGORY_ALREADY_ASSIGNED = + "report.error.reportCategory.already.assigned"; + +} diff --git a/src/main/java/org/openlmis/report/repository/DashboardReportRepository.java b/src/main/java/org/openlmis/report/repository/DashboardReportRepository.java new file mode 100644 index 0000000..5b418e3 --- /dev/null +++ b/src/main/java/org/openlmis/report/repository/DashboardReportRepository.java @@ -0,0 +1,54 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.repository; + +import java.util.List; +import java.util.UUID; +import org.openlmis.report.domain.DashboardReport; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; + +public interface DashboardReportRepository + extends PagingAndSortingRepository { + + Page findByEnabled(boolean enabled, Pageable pageable); + + boolean existsByName(String name); + + List findByShowOnHomePage(boolean showOnHomePage); + + boolean existsByCategory_Id(UUID categoryId); + + /** + * Retrieves a page of dashboard reports for which user has rights. If showOnHomePage + * was provided and user has rights, the home page report will be returned. + * + * @param pageable Pageable parameters for pagination. + * @param rightNames Names of user rights. + * @param showOnHomePage Filter to only include report that is shown on the home page. + * @return A page of dashboard reports matching the criteria. + */ + @Query("SELECT r FROM DashboardReport r WHERE r.enabled = true " + + "AND r.rightName IN :rightNames " + + "AND (:showOnHomePage IS NULL OR r.showOnHomePage = :showOnHomePage)") + Page findByRightsAndShowOnHomePage( + @Param("rightNames") List rightNames, + @Param("showOnHomePage") Boolean showOnHomePage, + Pageable pageable); +} diff --git a/src/main/java/org/openlmis/report/repository/JasperTemplateRepository.java b/src/main/java/org/openlmis/report/repository/JasperTemplateRepository.java index 25715e6..c5dba31 100644 --- a/src/main/java/org/openlmis/report/repository/JasperTemplateRepository.java +++ b/src/main/java/org/openlmis/report/repository/JasperTemplateRepository.java @@ -27,4 +27,6 @@ public interface JasperTemplateRepository JasperTemplate findByName(@Param("name") String name); List findByVisible(boolean visible); + + boolean existsByCategory_Id(UUID categoryId); } diff --git a/src/main/java/org/openlmis/report/repository/ReportCategoryRepository.java b/src/main/java/org/openlmis/report/repository/ReportCategoryRepository.java new file mode 100644 index 0000000..f1313d1 --- /dev/null +++ b/src/main/java/org/openlmis/report/repository/ReportCategoryRepository.java @@ -0,0 +1,30 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.repository; + +import java.util.Optional; +import java.util.UUID; +import org.openlmis.report.domain.ReportCategory; +import org.springframework.data.repository.PagingAndSortingRepository; + +public interface ReportCategoryRepository + extends PagingAndSortingRepository { + Optional findByName(String name); + + boolean existsByName(String name); + + boolean existsByIdIsNotAndName(UUID id, String name); +} diff --git a/src/main/java/org/openlmis/report/service/AuthorizationService.java b/src/main/java/org/openlmis/report/service/AuthorizationService.java index 6fbf7f5..d0eae67 100644 --- a/src/main/java/org/openlmis/report/service/AuthorizationService.java +++ b/src/main/java/org/openlmis/report/service/AuthorizationService.java @@ -74,5 +74,4 @@ public String obtainAccessToken() { void setRestTemplate(RestOperations restTemplate) { this.restTemplate = restTemplate; } - } diff --git a/src/main/java/org/openlmis/report/service/BaseCommunicationService.java b/src/main/java/org/openlmis/report/service/BaseCommunicationService.java index 48c5cd2..33159b2 100644 --- a/src/main/java/org/openlmis/report/service/BaseCommunicationService.java +++ b/src/main/java/org/openlmis/report/service/BaseCommunicationService.java @@ -241,4 +241,21 @@ private DataRetrievalException buildDataRetrievalException(HttpStatusCodeExcepti ex.getStatusCode(), ex.getResponseBodyAsString()); } + + protected

void runWithTokenRetry(HttpTask

task) { + try { + task.run(); + } catch (HttpStatusCodeException ex) { + if (HttpStatus.UNAUTHORIZED == ex.getStatusCode()) { + task.run(); + return; + } + throw ex; + } + } + + @FunctionalInterface + protected interface HttpTask { + void run(); + } } diff --git a/src/main/java/org/openlmis/report/service/DashboardReportService.java b/src/main/java/org/openlmis/report/service/DashboardReportService.java new file mode 100644 index 0000000..d01ef58 --- /dev/null +++ b/src/main/java/org/openlmis/report/service/DashboardReportService.java @@ -0,0 +1,282 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.service; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.openlmis.report.domain.DashboardReport; +import org.openlmis.report.domain.ReportCategory; +import org.openlmis.report.dto.DashboardReportDto; +import org.openlmis.report.dto.external.referencedata.RightDto; +import org.openlmis.report.dto.external.referencedata.RightType; +import org.openlmis.report.exception.NotFoundMessageException; +import org.openlmis.report.exception.ValidationMessageException; +import org.openlmis.report.i18n.DashboardReportMessageKeys; +import org.openlmis.report.i18n.ReportCategoryMessageKeys; +import org.openlmis.report.repository.DashboardReportRepository; +import org.openlmis.report.repository.ReportCategoryRepository; +import org.openlmis.report.service.referencedata.RightReferenceDataService; +import org.openlmis.report.utils.Message; +import org.openlmis.report.utils.Pagination; +import org.openlmis.report.utils.PropertyKeyUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +@Service +public class DashboardReportService { + private final ReportCategoryRepository reportCategoryRepository; + private final DashboardReportRepository dashboardReportRepository; + private final RightReferenceDataService rightReferenceDataService; + private final PermissionService permissionService; + + /** + * Constructs a new {@link ReportCategoryService} with the specified dependencies. + * + * @param reportCategoryRepository The repository responsible for handling report category data. + * @param permissionService The service responsible for checking and managing user permissions. + * @param dashboardReportRepository The repository for managing dashboard reports. + */ + @Autowired + public DashboardReportService(ReportCategoryRepository reportCategoryRepository, + PermissionService permissionService, DashboardReportRepository dashboardReportRepository, + RightReferenceDataService rightReferenceDataService) { + this.reportCategoryRepository = reportCategoryRepository; + this.dashboardReportRepository = dashboardReportRepository; + this.rightReferenceDataService = rightReferenceDataService; + this.permissionService = permissionService; + } + + /** + * Retrieves a dashboard report by its ID. + * + * @param reportId UUID of the dashboard report to retrieve. + * @return DashboardReportDto containing the dashboard report details. + */ + public DashboardReportDto getDashboardReport(UUID reportId) { + permissionService.canManageReports(); + + DashboardReport dashboardReport = dashboardReportRepository.findById(reportId) + .orElseThrow(() -> new NotFoundMessageException(new Message( + DashboardReportMessageKeys.ERROR_DASHBOARD_REPORT_NOT_FOUND, reportId))); + + return DashboardReportDto.newInstance(dashboardReport); + } + + /** + * Retrieves a page of dashboard reports, optionally filtered by the showOnHomePage parameter. + * + * @param pageable Pageable parameters for pagination. + * @return A page of dashboard reports matching the criteria. + */ + public Page getDashboardReports(Pageable pageable) { + permissionService.canManageReports(); + Page dashboardReports = dashboardReportRepository.findAll(pageable); + return Pagination.getPage(dashboardReports.map(DashboardReportDto::newInstance),pageable); + } + + /** + * Retrieves a page of dashboard reports for which user has rights, or retrieves + * a home page report. + * + * @param pageable Pageable parameters for pagination. + * @param showOnHomePage Filter to only include report that is shown on the home page. + * @return A page of dashboard reports matching the criteria. + */ + public Page getDashboardReportsForUser(Pageable pageable, + boolean showOnHomePage) { + permissionService.canViewReports(); + + List accessibleRights = permissionService.filterRightsForUser( + dashboardReportRepository.findByEnabled(true, pageable).getContent().stream() + .map(DashboardReport::getRightName) + .filter(Objects::nonNull) + .collect(Collectors.toList()) + ); + + if (accessibleRights.isEmpty()) { + return new PageImpl<>(Collections.emptyList(), pageable, 0); + } + + Page dashboardReports = dashboardReportRepository + .findByRightsAndShowOnHomePage( + accessibleRights, + showOnHomePage ? Boolean.TRUE : null, + pageable + ); + + return Pagination.getPage(dashboardReports.map(DashboardReportDto::newInstance), pageable); + } + + /** + * Deletes the dashboard report with the given ID. + * + * @param reportId UUID of the dashboard report to delete. + * @throws NotFoundMessageException if no dashboard report with the given ID is found. + */ + public void deleteDashboardReport(UUID reportId) { + permissionService.canManageReports(); + + DashboardReport dashboardReport = dashboardReportRepository.findById(reportId) + .orElseThrow(() -> new NotFoundMessageException(new Message( + DashboardReportMessageKeys.ERROR_DASHBOARD_REPORT_NOT_FOUND, reportId))); + + RightDto foundRight = rightReferenceDataService.findRight(dashboardReport.getRightName()); + rightReferenceDataService.delete(foundRight.getId()); + dashboardReportRepository.delete(dashboardReport); + } + + /** + * Adds a new dashboard report to the database. + * + * @param dto Data transfer object containing Dashboard Report data. + * @return Saved dashboard report. + */ + public DashboardReportDto createDashboardReport(DashboardReportDto dto) { + permissionService.canManageReports(); + + boolean reportExists = dashboardReportRepository.existsByName(dto.getName()); + if (reportExists) { + throw new ValidationMessageException(new Message( + DashboardReportMessageKeys.ERROR_DASHBOARD_REPORT_NAME_DUPLICATED, dto.getName())); + } + + DashboardReport dashboardReportToCreate = new DashboardReport(); + updateFrom(dto, dashboardReportToCreate); + dashboardReportToCreate.setId(null); + + RightDto rightToSave = createRight(dashboardReportToCreate.getName()); + dashboardReportToCreate.setRightName(rightToSave.getName()); + + try { + rightReferenceDataService.save(rightToSave); + } catch (Exception ex) { + throw new ValidationMessageException(new Message( + DashboardReportMessageKeys.ERROR_COULD_NOT_SAVE_RIGHT), ex); + } + + try { + return DashboardReportDto.newInstance(saveDashboardReport(dashboardReportToCreate)); + } catch (DataIntegrityViolationException ex) { + throw new ValidationMessageException(new Message( + DashboardReportMessageKeys.ERROR_DASHBOARD_REPORT_NAME_DUPLICATED, dto.getName()), ex); + } + } + + /** + * Updates a dashboard report in the database. + * + * @param id The UUID of the dashboard report to update. + * @param dashboardReportDto The DTO containing the updated dashboard report data. + * @return The updated DashboardReportDto. + */ + public DashboardReportDto updateDashboardReport(UUID id, DashboardReportDto dashboardReportDto) { + permissionService.canManageReports(); + + if (dashboardReportDto.getId() != null && !Objects.equals(dashboardReportDto.getId(), id)) { + throw new ValidationMessageException(new Message( + DashboardReportMessageKeys.ERROR_DASHBOARD_REPORT_ID_MISMATCH)); + } + + DashboardReport dashboardReport = dashboardReportRepository.findById(id) + .orElseThrow(() -> new NotFoundMessageException(new Message( + DashboardReportMessageKeys.ERROR_DASHBOARD_REPORT_NOT_FOUND, id))); + + updateFrom(dashboardReportDto, dashboardReport); + saveDashboardReport(dashboardReport); + + return DashboardReportDto.newInstance(dashboardReport); + } + + /** + * Update a dashboard report with the data from DTO. + * + * @param newReport The DashboardReportDto containing the category ID. + * @param reportToUpdate Report which is being updated. + */ + private void updateFrom(DashboardReportDto newReport, DashboardReport reportToUpdate) { + reportToUpdate.updateFrom(newReport); + reportToUpdate.setCategory(getCategoryOrThrow(newReport)); + } + + /** + * Retrieves the ReportCategory from the database using the category ID + * from the provided DTO. If the category doesn't exist, throws a + * NotFoundMessageException. + * + * @param newReport The DashboardReportDto containing the category ID + * @return The corresponding ReportCategory + * @throws NotFoundMessageException if the category doesn't exist + */ + private ReportCategory getCategoryOrThrow(DashboardReportDto newReport) { + if (newReport.getCategory() == null) { + throw new NotFoundMessageException(new Message( + ReportCategoryMessageKeys.ERROR_REPORT_CATEGORY_NOT_FOUND)); + } + + return reportCategoryRepository.findById(newReport.getCategory().getId()) + .orElseThrow(() -> new NotFoundMessageException(new Message( + ReportCategoryMessageKeys.ERROR_REPORT_CATEGORY_NOT_FOUND))); + } + + /** + * Creates right for new dashboard report. + * + * @param dashboardReportName Name of the dashboard report used to create right name + * @return New RightDto + */ + private RightDto createRight(String dashboardReportName) { + String transformedName = PropertyKeyUtil.transformToPropertyKey(dashboardReportName); + + if (null == transformedName) { + throw new ValidationMessageException(new Message( + DashboardReportMessageKeys.ERROR_COULD_NOT_SAVE_RIGHT)); + } + + RightDto rightToSave = new RightDto(); + rightToSave.setName(transformedName); + rightToSave.setType(RightType.REPORTS); + return rightToSave; + } + + /** + * Saves dashboard report and validates if any + * report has showOnHomepage set to true and overrides it. + */ + public DashboardReport saveDashboardReport(DashboardReport dashboardReport) { + DashboardReport dashboardReportToReturn = dashboardReportRepository.save(dashboardReport); + + if (dashboardReport.isShowOnHomePage()) { + List existingReports = dashboardReportRepository.findByShowOnHomePage(true); + + existingReports.stream() + .filter(report -> !report.getId().equals(dashboardReportToReturn.getId())) + .forEach(report -> { + report.setShowOnHomePage(false); + dashboardReportRepository.save(report); + }); + } + + return dashboardReportToReturn; + } +} diff --git a/src/main/java/org/openlmis/report/service/JasperTemplateService.java b/src/main/java/org/openlmis/report/service/JasperTemplateService.java index ade9d5c..34d89d8 100644 --- a/src/main/java/org/openlmis/report/service/JasperTemplateService.java +++ b/src/main/java/org/openlmis/report/service/JasperTemplateService.java @@ -37,6 +37,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import javax.imageio.ImageIO; @@ -48,12 +49,15 @@ import org.openlmis.report.domain.JasperTemplate; import org.openlmis.report.domain.JasperTemplateParameter; import org.openlmis.report.domain.JasperTemplateParameterDependency; +import org.openlmis.report.domain.ReportCategory; import org.openlmis.report.domain.ReportImage; import org.openlmis.report.exception.JasperReportViewException; import org.openlmis.report.exception.ReportingException; import org.openlmis.report.exception.ValidationMessageException; +import org.openlmis.report.i18n.ReportCategoryMessageKeys; import org.openlmis.report.i18n.ReportImageMessageKeys; import org.openlmis.report.repository.JasperTemplateRepository; +import org.openlmis.report.repository.ReportCategoryRepository; import org.openlmis.report.repository.ReportImageRepository; import org.openlmis.report.service.referencedata.RightReferenceDataService; import org.openlmis.report.utils.Message; @@ -78,6 +82,9 @@ public class JasperTemplateService { @Autowired private ReportImageRepository reportImageRepository; + @Autowired + private ReportCategoryRepository reportCategoryRepository; + /** * Saves a template with given name. * If template already exists, only description and required rights are updated. @@ -88,22 +95,30 @@ public class JasperTemplateService { * @return saved report template */ public JasperTemplate saveTemplate( - MultipartFile file, String name, String description, List requiredRights) - throws ReportingException { + MultipartFile file, String name, String description, List requiredRights, + String category) throws ReportingException { validateRequiredRights(requiredRights); JasperTemplate jasperTemplate = jasperTemplateRepository.findByName(name); + Optional reportCategory = reportCategoryRepository.findByName(category); + if (!reportCategory.isPresent()) { + throw new ReportingException( + ReportCategoryMessageKeys.ERROR_REPORT_CATEGORY_NOT_FOUND); + } + if (jasperTemplate == null) { jasperTemplate = JasperTemplate.builder() .name(name) .type(DEFAULT_REPORT_TYPE) .description(description) .requiredRights(requiredRights) + .category(reportCategory.get()) .build(); } else { jasperTemplate.setDescription(description); jasperTemplate.getRequiredRights().clear(); jasperTemplate.getRequiredRights().addAll(requiredRights); + jasperTemplate.setCategory(reportCategory.get()); } validateFileAndSaveTemplate(jasperTemplate, file); diff --git a/src/main/java/org/openlmis/report/service/PermissionService.java b/src/main/java/org/openlmis/report/service/PermissionService.java index a371656..1d38897 100644 --- a/src/main/java/org/openlmis/report/service/PermissionService.java +++ b/src/main/java/org/openlmis/report/service/PermissionService.java @@ -17,6 +17,9 @@ import static org.openlmis.report.i18n.PermissionMessageKeys.ERROR_NO_PERMISSION; +import java.util.ArrayList; +import java.util.List; + import org.openlmis.report.dto.external.ResultDto; import org.openlmis.report.dto.external.referencedata.RightDto; import org.openlmis.report.dto.external.referencedata.UserDto; @@ -30,6 +33,8 @@ @Service public class PermissionService { static final String REPORT_TEMPLATES_EDIT = "REPORT_TEMPLATES_EDIT"; + static final String REPORTS_MANAGE = "REPORTS_MANAGE"; + static final String REPORT_CATEGORIES_MANAGE = "REPORT_CATEGORIES_MANAGE"; public static final String REPORTS_VIEW = "REPORTS_VIEW"; @Autowired @@ -38,10 +43,27 @@ public class PermissionService { @Autowired private UserReferenceDataService userReferenceDataService; + /** + * Check whether the user has REPORT_TEMPLATES_EDIT permission. + */ public void canEditReportTemplates() { checkPermission(REPORT_TEMPLATES_EDIT); } + /** + * Check whether the user can manage reports - has MANAGE_DASHBOARD_REPORTS permission. + */ + public void canManageReports() { + checkPermission(REPORTS_MANAGE); + } + + /** + * Check whether the user can manage report categories - has MANAGE_REPORT_CATEGORIES permission. + */ + public void canManageReportCategories() { + checkPermission(REPORT_CATEGORIES_MANAGE); + } + /** * Check whether the user has REPORTS_VIEW permission. */ @@ -49,6 +71,19 @@ public void canViewReports() { checkPermission(REPORTS_VIEW); } + /** + * Filters rights for which user has permission. + */ + public List filterRightsForUser(List rightNames) { + List accessibleRights = new ArrayList<>(); + for (String rightName : rightNames) { + if (hasPermission(rightName)) { + accessibleRights.add(rightName); + } + } + return accessibleRights; + } + /** * Checks whether user has the given set of rights. * Throws {@link PermissionMessageException} if doesn't have all of these permissions. diff --git a/src/main/java/org/openlmis/report/service/ReportCategoryService.java b/src/main/java/org/openlmis/report/service/ReportCategoryService.java new file mode 100644 index 0000000..153c943 --- /dev/null +++ b/src/main/java/org/openlmis/report/service/ReportCategoryService.java @@ -0,0 +1,158 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.service; + +import java.util.Optional; +import java.util.UUID; +import org.openlmis.report.domain.ReportCategory; +import org.openlmis.report.dto.ReportCategoryDto; +import org.openlmis.report.exception.NotFoundMessageException; +import org.openlmis.report.exception.ValidationMessageException; +import org.openlmis.report.i18n.ReportCategoryMessageKeys; +import org.openlmis.report.repository.DashboardReportRepository; +import org.openlmis.report.repository.JasperTemplateRepository; +import org.openlmis.report.repository.ReportCategoryRepository; +import org.openlmis.report.utils.Message; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +@Service +public class ReportCategoryService { + private final ReportCategoryRepository reportCategoryRepository; + private final DashboardReportRepository dashboardReportRepository; + private final JasperTemplateRepository jasperTemplateRepository; + private final PermissionService permissionService; + + /** + * Constructs a new {@link ReportCategoryService} with the specified dependencies. + * + * @param reportCategoryRepository The repository responsible for handling report category data. + * @param permissionService The service responsible for checking and managing user permissions. + * @param dashboardReportRepository The repository for managing dashboard reports. + * @param jasperTemplateRepository The repository for managing Jasper templates. + */ + @Autowired + public ReportCategoryService(ReportCategoryRepository reportCategoryRepository, + PermissionService permissionService, DashboardReportRepository dashboardReportRepository, + JasperTemplateRepository jasperTemplateRepository) { + this.reportCategoryRepository = reportCategoryRepository; + this.dashboardReportRepository = dashboardReportRepository; + this.jasperTemplateRepository = jasperTemplateRepository; + this.permissionService = permissionService; + } + + /** + * Get chosen report category. + * + * @param categoryId UUID of a report category we want to get. + */ + public ReportCategoryDto getReportCategoryById(UUID categoryId) { + permissionService.canManageReportCategories(); + + Optional reportCategory = reportCategoryRepository.findById(categoryId); + + if (!reportCategory.isPresent()) { + throw new NotFoundMessageException(ReportCategoryMessageKeys.ERROR_REPORT_CATEGORY_NOT_FOUND); + } + + return ReportCategoryDto.newInstance(reportCategory.get()); + } + + /** + * Get page of all report categories. + */ + public Page getAllReportCategories(Pageable pageable) { + permissionService.canManageReportCategories(); + + Page reportCategories = reportCategoryRepository.findAll(pageable); + + return reportCategories.map(ReportCategoryDto::newInstance); + } + + /** + * Create a new report category. + * + * @param dto Data transfer object containing Report Category data. + */ + public ReportCategoryDto createReportCategory(ReportCategoryDto dto) { + permissionService.canManageReportCategories(); + + boolean categoryExists = reportCategoryRepository.existsByName(dto.getName()); + if (categoryExists) { + throw new ValidationMessageException(new Message( + ReportCategoryMessageKeys.ERROR_REPORT_CATEGORY_NAME_DUPLICATED, dto.getName())); + } + + ReportCategory reportCategoryToSave = ReportCategory.newInstance(dto); + ReportCategory savedCategory = reportCategoryRepository.save(reportCategoryToSave); + + return ReportCategoryDto.newInstance(savedCategory); + } + + /** + * Update an existing report category. + * + * @param id UUID of a report category to update. + * @param dto Data transfer object containing Report Category data. + */ + public ReportCategoryDto updateReportCategory(UUID id, ReportCategoryDto dto) { + permissionService.canManageReportCategories(); + + if (dto.getId() != null && !dto.getId().equals(id)) { + throw new ValidationMessageException(new Message( + ReportCategoryMessageKeys.ERROR_REPORT_CATEGORY_ID_MISMATCH)); + } + + boolean nameAlreadyExists = reportCategoryRepository.existsByIdIsNotAndName(id, dto.getName()); + if (nameAlreadyExists) { + throw new ValidationMessageException( + new Message(ReportCategoryMessageKeys.ERROR_REPORT_CATEGORY_NAME_DUPLICATED)); + } + + ReportCategory existingCategory = reportCategoryRepository.findById(id) + .orElseThrow(() -> new NotFoundMessageException( + new Message(ReportCategoryMessageKeys.ERROR_REPORT_CATEGORY_NOT_FOUND, id))); + + existingCategory.updateFrom(dto); + return ReportCategoryDto.newInstance(reportCategoryRepository.save(existingCategory)); + } + + /** + * Allows deleting a report category. + * + * @param categoryId UUID of a report category we want to delete. + */ + public void deleteReportCategory(UUID categoryId) { + permissionService.canManageReportCategories(); + + boolean isAssignedToReports = dashboardReportRepository.existsByCategory_Id(categoryId); + boolean isAssignedToTemplates = jasperTemplateRepository.existsByCategory_Id(categoryId); + + if (isAssignedToReports || isAssignedToTemplates) { + throw new ValidationMessageException(new Message( + ReportCategoryMessageKeys.ERROR_CATEGORY_ALREADY_ASSIGNED, categoryId)); + } + + ReportCategory reportCategory = reportCategoryRepository.findById(categoryId) + .orElseThrow(() -> new NotFoundMessageException(new Message( + ReportCategoryMessageKeys.ERROR_REPORT_CATEGORY_NOT_FOUND, categoryId))); + + reportCategoryRepository.delete(reportCategory); + } + +} diff --git a/src/main/java/org/openlmis/report/service/referencedata/RightReferenceDataService.java b/src/main/java/org/openlmis/report/service/referencedata/RightReferenceDataService.java index ad46854..7b35553 100644 --- a/src/main/java/org/openlmis/report/service/referencedata/RightReferenceDataService.java +++ b/src/main/java/org/openlmis/report/service/referencedata/RightReferenceDataService.java @@ -15,17 +15,27 @@ package org.openlmis.report.service.referencedata; +import static org.openlmis.report.utils.RequestHelper.createUri; + import java.util.List; +import java.util.UUID; + import org.openlmis.report.dto.external.referencedata.RightDto; +import org.openlmis.report.exception.ValidationMessageException; +import org.openlmis.report.i18n.DashboardReportMessageKeys; +import org.openlmis.report.utils.Message; +import org.openlmis.report.utils.RequestHelper; import org.openlmis.report.utils.RequestParameters; +import org.springframework.http.HttpMethod; import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpStatusCodeException; @Service public class RightReferenceDataService extends BaseReferenceDataService { @Override protected String getUrl() { - return "/api/rights/"; + return "/api/rights"; } @Override @@ -45,7 +55,64 @@ protected Class getArrayResultClass() { * @return right related with the name or {@code null}. */ public RightDto findRight(String name) { - List rights = findAll("search", RequestParameters.init().set("name", name)); + List rights = findAll("/search", RequestParameters.init().set("name", name)); return rights.isEmpty() ? null : rights.get(0); } + + /** + * Save a new right by making a POST request to the referenced data service. + * + * @param rightDto the RightDto to save + */ + @SuppressWarnings("PMD.PreserveStackTrace") + public void find(RightDto rightDto) { + + } + + /** + * Save a new right by making a POST request to the referenced data service. + * + * @param rightDto the RightDto to save + */ + @SuppressWarnings("PMD.PreserveStackTrace") + public void save(RightDto rightDto) { + String url = getServiceUrl() + getUrl(); + + try { + runWithTokenRetry(() -> + restTemplate.exchange( + createUri(url), + HttpMethod.PUT, + RequestHelper.createEntity(authorizationService.obtainAccessToken(), rightDto), + RightDto.class + )); + } catch (HttpStatusCodeException ex) { + throw new ValidationMessageException( + new Message(DashboardReportMessageKeys.ERROR_COULD_NOT_SAVE_RIGHT, rightDto.getName())); + } + } + + /** + * Delete an existing right by making a DELETE request to the referencedata service. + * + * @param rightId the ID of the RightDto to delete + */ + @SuppressWarnings("PMD.PreserveStackTrace") + public void delete(UUID rightId) { + String url = getServiceUrl() + getUrl() + "/" + rightId; + + try { + runWithTokenRetry(() -> + restTemplate.exchange( + createUri(url), + HttpMethod.DELETE, + RequestHelper.createEntity(authorizationService.obtainAccessToken(), null), + Void.class + ) + ); + } catch (HttpStatusCodeException ex) { + throw new ValidationMessageException( + new Message(DashboardReportMessageKeys.ERROR_COULD_NOT_DELETE_RIGHT, rightId, ex)); + } + } } diff --git a/src/main/java/org/openlmis/report/utils/PropertyKeyUtil.java b/src/main/java/org/openlmis/report/utils/PropertyKeyUtil.java new file mode 100644 index 0000000..eb377e1 --- /dev/null +++ b/src/main/java/org/openlmis/report/utils/PropertyKeyUtil.java @@ -0,0 +1,50 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.utils; + +import java.util.regex.Pattern; + +public class PropertyKeyUtil { + + private static final Pattern INVALID_CHARACTERS_PATTERN = Pattern.compile("[^a-zA-Z0-9_]"); + + /** + * Transforms a string to a valid property key format. + * - Trims whitespace + * - Converts to uppercase + * - Replaces spaces with underscores + * - Removes invalid characters + * - Ensures the key does not start with a number + * + * @param nameToTransform input string + * @return the valid name which can be used as property key + */ + public static String transformToPropertyKey(String nameToTransform) { + if (nameToTransform == null || nameToTransform.trim().isEmpty()) { + return null; + } + + String transformed = nameToTransform.trim().toUpperCase().replace(" ", "_"); + + transformed = INVALID_CHARACTERS_PATTERN.matcher(transformed).replaceAll(""); + + if (Character.isDigit(transformed.charAt(0))) { + transformed = "_" + transformed; + } + + return transformed + "_RIGHT"; + } +} diff --git a/src/main/java/org/openlmis/report/utils/ReportType.java b/src/main/java/org/openlmis/report/utils/ReportType.java new file mode 100644 index 0000000..258884f --- /dev/null +++ b/src/main/java/org/openlmis/report/utils/ReportType.java @@ -0,0 +1,21 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.utils; + +public enum ReportType { + POWERBI, + SUPERSET +} diff --git a/src/main/java/org/openlmis/report/web/DashboardReportController.java b/src/main/java/org/openlmis/report/web/DashboardReportController.java new file mode 100644 index 0000000..ac9fbdd --- /dev/null +++ b/src/main/java/org/openlmis/report/web/DashboardReportController.java @@ -0,0 +1,124 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.web; + +import static org.openlmis.report.web.DashboardReportController.RESOURCE_PATH; + +import java.util.UUID; +import javax.validation.Valid; +import org.openlmis.report.dto.DashboardReportDto; +import org.openlmis.report.service.DashboardReportService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +@Controller +@Transactional +@RequestMapping(RESOURCE_PATH) +public class DashboardReportController extends BaseController { + public static final String RESOURCE_PATH = "/api/reports/dashboardReports"; + + @Autowired + private DashboardReportService dashboardReportService; + + /** + * Get chosen dashboard report. + * + * @param reportId UUID of dashboard report we want to get. + */ + @GetMapping(value = "/{id}") + @ResponseStatus(HttpStatus.OK) + @ResponseBody + public DashboardReportDto getDashboardReport(@PathVariable("id") UUID reportId) { + return dashboardReportService.getDashboardReport(reportId); + } + + /** + * Get page of all dashboard reports. + * + * @return All dashboard reports matching certain criteria. + */ + @GetMapping + @ResponseStatus(HttpStatus.OK) + @ResponseBody + public Page getDashboardReports(Pageable pageable) { + return dashboardReportService.getDashboardReports(pageable); + } + + /** + * Get page of all dashboard reports for which user has rights. + * + * @return All dashboard reports. + */ + @GetMapping(value = "/availableReports") + @ResponseStatus(HttpStatus.OK) + @ResponseBody + public Page getDashboardReportsForUser(Pageable pageable, + @RequestParam(required = false, defaultValue = "false") boolean showOnHomePage) { + return dashboardReportService.getDashboardReportsForUser(pageable, showOnHomePage); + } + + /** + * Allows deleting dashboard report. + * + * @param reportId UUID of dashboard report to delete. + */ + @DeleteMapping(value = "/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteDashboardReport(@PathVariable("id") UUID reportId) { + dashboardReportService.deleteDashboardReport(reportId); + } + + /** + * Adds or updates dashboard report to database. + * + * @param dto Data transfer object containing Dashboard Report data. + */ + @PostMapping + @ResponseStatus(HttpStatus.OK) + @ResponseBody + public DashboardReportDto createDashboardReport(@Valid @RequestBody DashboardReportDto dto) { + return dashboardReportService.createDashboardReport(dto); + } + + /** + * Allows updating a dashboard report. + * + * @param id The UUID of the dashboard report to update. + * @param dashboardReportDto Data transfer object containing updated dashboard report data. + * @return The updated DashboardReportDto. + */ + @PutMapping(value = "/{id}") + @ResponseStatus(HttpStatus.OK) + @ResponseBody + public DashboardReportDto updateDashboardReport(@PathVariable("id") UUID id, + @Valid @RequestBody DashboardReportDto dashboardReportDto) { + return dashboardReportService.updateDashboardReport(id, dashboardReportDto); + } +} diff --git a/src/main/java/org/openlmis/report/web/JasperTemplateController.java b/src/main/java/org/openlmis/report/web/JasperTemplateController.java index 784d023..9061e0f 100644 --- a/src/main/java/org/openlmis/report/web/JasperTemplateController.java +++ b/src/main/java/org/openlmis/report/web/JasperTemplateController.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.UUID; import javax.servlet.http.HttpServletRequest; + import org.openlmis.report.domain.JasperTemplate; import org.openlmis.report.dto.JasperTemplateDto; import org.openlmis.report.exception.JasperReportViewException; @@ -98,7 +99,7 @@ public class JasperTemplateController extends BaseController { @ResponseStatus(HttpStatus.OK) public void createJasperReportTemplate( @RequestPart("file") MultipartFile file, String name, String description, - String[] requiredRights) throws ReportingException { + String[] requiredRights, String category) throws ReportingException { permissionService.canEditReportTemplates(); LOGGER.debug("Saving template with name: " + name); @@ -107,7 +108,7 @@ public void createJasperReportTemplate( ? Collections.emptyList() : Arrays.asList(requiredRights); JasperTemplate template = jasperTemplateService - .saveTemplate(file, name, description, rightList); + .saveTemplate(file, name, description, rightList, category); LOGGER.debug("Saved template with id: " + template.getId()); } @@ -185,8 +186,7 @@ public ResponseEntity generateReport( } List requiredRights = template.getRequiredRights(); - permissionService.validatePermissions( - requiredRights.toArray(new String[requiredRights.size()])); + permissionService.validatePermissions(requiredRights.toArray(new String[0])); Map map = jasperTemplateService.mapRequestParametersToTemplate( request, template diff --git a/src/main/java/org/openlmis/report/web/ReportCategoryController.java b/src/main/java/org/openlmis/report/web/ReportCategoryController.java new file mode 100644 index 0000000..1e5be94 --- /dev/null +++ b/src/main/java/org/openlmis/report/web/ReportCategoryController.java @@ -0,0 +1,109 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.web; + +import static org.openlmis.report.web.ReportCategoryController.RESOURCE_PATH; + +import java.util.UUID; +import javax.validation.Valid; + +import org.openlmis.report.dto.ReportCategoryDto; +import org.openlmis.report.service.ReportCategoryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +@Controller +@Transactional +@RequestMapping(RESOURCE_PATH) +public class ReportCategoryController extends BaseController { + + public static final String RESOURCE_PATH = "/api/reports/reportCategories"; + + @Autowired + private ReportCategoryService reportCategoryService; + + /** + * Get chosen report category. + * + * @param categoryId UUID of a report category we want to get. + */ + @GetMapping(value = "/{id}") + @ResponseStatus(HttpStatus.OK) + @ResponseBody + public ReportCategoryDto getReportCategory(@PathVariable("id") UUID categoryId) { + return reportCategoryService.getReportCategoryById(categoryId); + } + + /** + * Get page of all report categories. + */ + @GetMapping + @ResponseStatus(HttpStatus.OK) + @ResponseBody + public Page getAllReportCategories(Pageable pageable) { + return reportCategoryService.getAllReportCategories(pageable); + } + + /** + * Create a new report category. + * + * @param dto Data transfer object containing Report Category data. + */ + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + @ResponseBody + public ReportCategoryDto createReportCategory(@Valid @RequestBody ReportCategoryDto dto) { + return reportCategoryService.createReportCategory(dto); + } + + /** + * Update an existing report category. + * + * @param id UUID of a report category to update. + * @param dto Data transfer object containing Report Category data. + */ + @PutMapping(value = "/{id}") + @ResponseStatus(HttpStatus.OK) + @ResponseBody + public ReportCategoryDto updateReportCategory(@PathVariable("id") UUID id, + @Valid @RequestBody ReportCategoryDto dto) { + return reportCategoryService.updateReportCategory(id, dto); + } + + /** + * Allows deleting a report category. + * + * @param categoryId UUID of a report category we want to delete. + */ + @DeleteMapping(value = "/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteReportCategory(@PathVariable("id") UUID categoryId) { + reportCategoryService.deleteReportCategory(categoryId); + } +} diff --git a/src/main/resources/api-definition.yaml b/src/main/resources/api-definition.yaml index 7f1543e..b56aaeb 100644 --- a/src/main/resources/api-definition.yaml +++ b/src/main/resources/api-definition.yaml @@ -59,6 +59,14 @@ schemas: - stockAdjustmentReason: !include schemas/stockAdjustmentReason.json + - dashboardReportDto: !include schemas/dashboardReportDto.json + + - dashboardReportDtoPage: !include schemas/dashboardReportDtoPage.json + + - reportCategoryDto: !include schemas/reportCategoryDto.json + + - reportCategoryDtoPage: !include schemas/reportCategoryDtoPage.json + - stockAdjustmentReasonArray: | { "type": "array", @@ -140,7 +148,6 @@ resourceTypes: 200: /api: - /reports: /requisitions/{id}/print: uriParameters: @@ -232,20 +239,25 @@ resourceTypes: multipart/form-data: formParameters: file: - displayName: Template with .jrxml format - type: file - required: false - repeat: false + displayName: Template with .jrxml format + type: file + required: false + repeat: false name: - displayName: Name of Template in database - type: string - required: false - repeat: false + displayName: Name of Template in database + type: string + required: false + repeat: false description: - displayName: Description of Template - type: string - required: false - repeat: false + displayName: Description of Template + type: string + required: false + repeat: false + category: + displayName: Category name of the template + type: string + required: false + repeat: false responses: 200: 403: @@ -411,7 +423,191 @@ resourceTypes: body: application/json: schema: localizedMessage - + /dashboardReports: + displayName: Dashboard reports + get: + is: [ secured, paginated ] + description: Get all dashboard reports matching certain criteria. + responses: + 200: + body: + application/json: + schema: dashboardReportDtoPage + 403: + body: + application/json: + schema: localizedMessage + post: + is: [ secured ] + description: Create dashboard report. + body: + application/json: + schema: dashboardReportDto + responses: + 200: + body: + application/json: + schema: dashboardReportDto + 403: + body: + application/json: + schema: localizedMessage + put: + is: [ secured ] + description: Update dashboard report. + body: + application/json: + schema: dashboardReportDto + responses: + 200: + body: + application/json: + schema: dashboardReportDto + 404: + body: + application/json: + schema: localizedMessage + /{id}: + uriParameters: + id: + displayName: id + type: string + required: true + repeat: false + get: + is: [ secured ] + description: Get chosen dashboard report. + responses: + 200: + body: + application/json: + schema: dashboardReportDto + 401: + body: + application/json: + schema: localizedMessage + 403: + body: + application/json: + schema: localizedMessage + 404: + body: + application/json: + schema: localizedMessage + delete: + is: [ secured ] + description: Delete chosen dashboard report. + responses: + 204: + 403: + body: + application/json: + schema: localizedMessage + 404: + body: + application/json: + schema: localizedMessage + /availableReports: + get: + is: [ secured ] + description: Get dashboard reports permitted for user. + responses: + 200: + body: + application/json: + schema: dashboardReportDtoPage + 401: + body: + application/json: + schema: localizedMessage + /reportCategories: + displayName: Report Categories + get: + is: [ secured, paginated ] + description: Get all report categories matching certain criteria. + responses: + 200: + body: + application/json: + schema: reportCategoryDtoPage + 403: + body: + application/json: + schema: localizedMessage + post: + is: [ secured ] + description: Create report category. + body: + application/json: + schema: reportCategoryDto + responses: + 200: + body: + application/json: + schema: reportCategoryDto + 403: + body: + application/json: + schema: localizedMessage + put: + is: [ secured ] + description: Update report category. + body: + application/json: + schema: reportCategoryDto + responses: + 200: + body: + application/json: + schema: reportCategoryDto + 403: + body: + application/json: + schema: localizedMessage + 404: + body: + application/json: + schema: localizedMessage + /{id}: + uriParameters: + id: + displayName: id + type: string + required: true + repeat: false + get: + is: [ secured ] + description: Get chosen report category. + responses: + 200: + body: + application/json: + schema: dashboardReportDto + 401: + body: + application/json: + schema: localizedMessage + 403: + body: + application/json: + schema: localizedMessage + 404: + body: + application/json: + schema: localizedMessage + delete: + is: [ secured ] + description: Delete chosen report category. + responses: + 204: + 403: + body: + application/json: + schema: localizedMessage + 404: + body: + application/json: + schema: localizedMessage /settings: displayName: Settings. /{key}: diff --git a/src/main/resources/db/migration/20241210183445805__add_dashboard_reports_and_report_categories_tables.sql b/src/main/resources/db/migration/20241210183445805__add_dashboard_reports_and_report_categories_tables.sql new file mode 100755 index 0000000..d471b7b --- /dev/null +++ b/src/main/resources/db/migration/20241210183445805__add_dashboard_reports_and_report_categories_tables.sql @@ -0,0 +1,27 @@ +CREATE TABLE IF NOT EXISTS report.report_categories ( + id UUID PRIMARY KEY NOT NULL, + name text UNIQUE NOT NULL +); + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM report.report_categories + WHERE name = 'Default Category' + ) THEN + INSERT INTO report.report_categories (id, name) VALUES ('55e19d72-4d0b-4453-b627-2f3477681c24', 'Default Category'); + END IF; +END $$; + +CREATE TABLE IF NOT EXISTS report.dashboard_reports ( + id UUID PRIMARY KEY NOT NULL, + name text UNIQUE NOT NULL, + url text NOT NULL, + type text NOT NULL, + enabled boolean NOT NULL, + showOnHomePage boolean NOT NULL, + categoryId UUID NOT NULL, + rightName text UNIQUE NOT NULL, + CONSTRAINT fk_report_categories FOREIGN KEY (categoryId) REFERENCES report.report_categories (id) ON DELETE RESTRICT +); \ No newline at end of file diff --git a/src/main/resources/db/migration/20241211173004911__update_jasper_templates_table.sql b/src/main/resources/db/migration/20241211173004911__update_jasper_templates_table.sql new file mode 100755 index 0000000..665bebe --- /dev/null +++ b/src/main/resources/db/migration/20241211173004911__update_jasper_templates_table.sql @@ -0,0 +1,16 @@ +ALTER TABLE report.jasper_templates ADD COLUMN categoryId UUID; + +UPDATE report.jasper_templates +SET categoryId = ( + SELECT id + FROM report.report_categories + WHERE name = 'Default Category' + LIMIT 1 +) +WHERE categoryId IS NULL; + +ALTER TABLE report.jasper_templates ALTER COLUMN categoryId SET NOT NULL; + +ALTER TABLE report.jasper_templates ADD CONSTRAINT fk_category +FOREIGN KEY (categoryId) REFERENCES report.report_categories (id) +ON DELETE SET NULL; diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties index 358fc97..c1501b2 100644 --- a/src/main/resources/messages_en.properties +++ b/src/main/resources/messages_en.properties @@ -5,4 +5,15 @@ report.error.authentication.user.notFound=User with id {0} can not be found. report.error.jasper.file.creation=Error while creating temporary Jasper file. report.error.jasper.template.notFound=Jasper template not found in the system. report.error.jasper.report.generation=Error while generating Jasper report. -report.error.jasper.report.format.unknown=Unknown report file format [{0}] specified. \ No newline at end of file +report.error.jasper.report.format.unknown=Unknown report file format [{0}] specified. + +report.error.dashboardReport.name.duplicated=Dashboard report with name {0} already exists. +report.error.dashboardReport.notFound=Dashboard report with id {0} can not be found. +report.error.dashboardReport.id.mismatch=Error dashboard report id mismatch. +report.error.dashboardReport.save.right.failed=Could not persist new right to the database when creating new dashboard report. Right name: {0} +report.error.dashboardReport.delete.right.failed=Could not delete right with id {0}. Make sure the right is unassigned from roles. + +report.error.reportCategory.name.duplicated=Error. Report category name duplicated. {0} +report.error.reportCategory.id.mismatch=Error. Report category id mismatch. +report.error.reportCategory.notFound=Report category with id {0} not found. +report.error.reportCategory.already.assigned=Report category cannot be deleted since it is assigned. {0} diff --git a/src/main/resources/schemas/dashboardReportDto.json b/src/main/resources/schemas/dashboardReportDto.json new file mode 100644 index 0000000..fc28d9e --- /dev/null +++ b/src/main/resources/schemas/dashboardReportDto.json @@ -0,0 +1,53 @@ +{ + "type": "object", + "$schema": "http://json-schema.org/draft-04/schema", + "title": "DashboardReportDto", + "description": "A single dashboard report", + "properties": { + "id": { + "type": "string", + "title": "id" + }, + "name": { + "type": "string", + "title": "name" + }, + "url": { + "type": "string", + "title": "url" + }, + "type": { + "type": "string", + "title": "type", + "enum": ["SUPERSET", "POWERBI"] + }, + "enabled": { + "type": "boolean", + "title": "enabled", + "description": "Indicates if the report is enabled" + }, + "showOnHomePage": { + "type": "boolean", + "title": "showOnHomePage", + "description": "Indicates if the report should be displayed on the homepage" + }, + "category": { + "type": "object", + "$ref": "reportCategoryDto.json" + }, + "rightName": { + "type": "string", + "title": "rightName" + } + }, + "required": [ + "id", + "name", + "url", + "type", + "enabled", + "showOnHomePage", + "category", + "rightName" + ] +} \ No newline at end of file diff --git a/src/main/resources/schemas/dashboardReportDtoPage.json b/src/main/resources/schemas/dashboardReportDtoPage.json new file mode 100644 index 0000000..ecd4d9d --- /dev/null +++ b/src/main/resources/schemas/dashboardReportDtoPage.json @@ -0,0 +1,25 @@ +{ + "type": "object", + "$schema": "http://json-schema.org/draft-04/schema", + "title": "Collection", + "description": "Paginated collection", + "properties": { + "content": { + "type": "array", + "items": { "type": "object", "$ref": "dashboardReportDto.json" } + }, + "totalPages": { "type": "integer", "title": "totalPages" }, + "totalElements": { "type": "integer", "title": "totalElements" }, + "size": { "type": "integer", "title": "size" }, + "number": { "type": "integer", "title": "number" }, + "numberOfElements": { "type": "integer", "title": "numberOfElements" }, + "last": { "type": "boolean", "title": "last" }, + "first": { "type": "boolean", "title": "first" }, + "sort?": { + "title": "sort", + "type": "array", + "items": { "type": "object" } + } + }, + "required": ["content" , "totalPages" , "totalElements", "size", "number" , "numberOfElements" , "first" , "last"] +} \ No newline at end of file diff --git a/src/main/resources/schemas/jasperTemplateDto.json b/src/main/resources/schemas/jasperTemplateDto.json index 14cd962..67c02f4 100755 --- a/src/main/resources/schemas/jasperTemplateDto.json +++ b/src/main/resources/schemas/jasperTemplateDto.json @@ -44,10 +44,15 @@ "description": { "type": ["string", "null"], "title": "description" + }, + "category": { + "type": "object", + "$ref": "reportCategoryDto.json" } - } , + }, "required": [ "id", - "name" + "name", + "category" ] } diff --git a/src/main/resources/schemas/reportCategoryDto.json b/src/main/resources/schemas/reportCategoryDto.json new file mode 100644 index 0000000..05b4a86 --- /dev/null +++ b/src/main/resources/schemas/reportCategoryDto.json @@ -0,0 +1,20 @@ +{ + "type": "object", + "$schema": "http://json-schema.org/draft-04/schema", + "title": "ReportCategoryDto", + "description": "A single report category", + "properties": { + "id": { + "type": "string", + "title": "id" + }, + "name": { + "type": "string", + "title": "name" + } + }, + "required": [ + "id", + "name" + ] +} \ No newline at end of file diff --git a/src/main/resources/schemas/reportCategoryDtoPage.json b/src/main/resources/schemas/reportCategoryDtoPage.json new file mode 100644 index 0000000..db96e18 --- /dev/null +++ b/src/main/resources/schemas/reportCategoryDtoPage.json @@ -0,0 +1,25 @@ +{ + "type": "object", + "$schema": "http://json-schema.org/draft-04/schema", + "title": "Collection", + "description": "Paginated collection", + "properties": { + "content": { + "type": "array", + "items": { "type": "object", "$ref": "reportCategoryDto.json" } + }, + "totalPages": { "type": "integer", "title": "totalPages" }, + "totalElements": { "type": "integer", "title": "totalElements" }, + "size": { "type": "integer", "title": "size" }, + "number": { "type": "integer", "title": "number" }, + "numberOfElements": { "type": "integer", "title": "numberOfElements" }, + "last": { "type": "boolean", "title": "last" }, + "first": { "type": "boolean", "title": "first" }, + "sort?": { + "title": "sort", + "type": "array", + "items": { "type": "object" } + } + }, + "required": ["content" , "totalPages" , "totalElements", "size", "number" , "numberOfElements" , "first" , "last"] +} \ No newline at end of file diff --git a/src/test/java/org/openlmis/report/domain/DashboardReportTest.java b/src/test/java/org/openlmis/report/domain/DashboardReportTest.java new file mode 100644 index 0000000..78a08c0 --- /dev/null +++ b/src/test/java/org/openlmis/report/domain/DashboardReportTest.java @@ -0,0 +1,86 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.openlmis.report.dto.DashboardReportDto; +import org.openlmis.report.utils.DashboardReportDataBuilder; + +public class DashboardReportTest { + @Test + public void shouldCreateNewInstance() { + DashboardReport dashboardReport = new DashboardReportDataBuilder().build(); + DashboardReportDto importer = DashboardReportDto.newInstance(dashboardReport); + + DashboardReport newInstance = DashboardReport.newInstance(importer); + + assertThat(importer.getName()).isEqualTo(newInstance.getName()); + assertThat(importer.getRightName()).isEqualTo((newInstance.getName() + "_RIGHT") + .toUpperCase()); + assertThat(importer.getUrl()).isEqualTo(newInstance.getUrl()); + assertThat(importer.getType()).isEqualTo(newInstance.getType()); + assertThat(importer.isEnabled()).isEqualTo(newInstance.isEnabled()); + assertThat(importer.isShowOnHomePage()).isEqualTo(newInstance.isShowOnHomePage()); + assertThat(importer.getCategory().getId()).isEqualTo(newInstance.getCategory().getId()); + } + + @Test + public void shouldExportData() { + DashboardReport dashboardReport = new DashboardReportDataBuilder().build(); + DashboardReportDto dto = new DashboardReportDto(); + + dashboardReport.export(dto); + + assertThat(dto.getName()).isEqualTo(dashboardReport.getName()); + assertThat(dto.getRightName()).isEqualTo((dashboardReport.getName() + "_RIGHT") + .toUpperCase()); + assertThat(dto.getUrl()).isEqualTo(dashboardReport.getUrl()); + assertThat(dto.getType()).isEqualTo(dashboardReport.getType()); + assertThat(dto.isEnabled()).isEqualTo(dashboardReport.isEnabled()); + assertThat(dto.isShowOnHomePage()).isEqualTo(dashboardReport.isShowOnHomePage()); + assertThat(dto.getCategory().getId()).isEqualTo(dashboardReport.getCategory().getId()); + } + + @Test + public void shouldHandleEnabledFlag() { + DashboardReportDto dto = new DashboardReportDto(); + dto.setEnabled(true); + + DashboardReport dashboardReport = DashboardReport.newInstance(dto); + + assertThat(dashboardReport.isEnabled()).isTrue(); + + dto.setEnabled(false); + dashboardReport = DashboardReport.newInstance(dto); + + assertThat(dashboardReport.isEnabled()).isFalse(); + } + + @Test + public void shouldBeEqualIfSameFields() { + DashboardReportDto dto = new DashboardReportDto(); + dto.setName("Test Report"); + dto.setUrl("http://example.com"); + + DashboardReport dashboardReport1 = DashboardReport.newInstance(dto); + DashboardReport dashboardReport2 = DashboardReport.newInstance(dto); + + assertThat(dashboardReport1).isEqualTo(dashboardReport2); + assertThat(dashboardReport1.hashCode()).isEqualTo(dashboardReport2.hashCode()); + } +} \ No newline at end of file diff --git a/src/test/java/org/openlmis/report/domain/ReportCategoryTest.java b/src/test/java/org/openlmis/report/domain/ReportCategoryTest.java new file mode 100644 index 0000000..5539c6d --- /dev/null +++ b/src/test/java/org/openlmis/report/domain/ReportCategoryTest.java @@ -0,0 +1,55 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.openlmis.report.dto.ReportCategoryDto; +import org.openlmis.report.utils.ReportCategoryDataBuilder; + +public class ReportCategoryTest { + + @Test + public void shouldCreateNewInstance() { + ReportCategory reportCategory = new ReportCategoryDataBuilder().build(); + ReportCategoryDto importer = ReportCategoryDto.newInstance(reportCategory); + ReportCategory newInstance = ReportCategory.newInstance(importer); + + assertThat(importer.getName()).isEqualTo(newInstance.getName()); + } + + @Test + public void shouldExportData() { + ReportCategory reportCategory = new ReportCategoryDataBuilder().build(); + ReportCategoryDto dto = new ReportCategoryDto(); + reportCategory.export(dto); + + assertThat(dto.getName()).isEqualTo(reportCategory.getName()); + } + + @Test + public void shouldBeEqualIfSameFields() { + ReportCategoryDto dto = new ReportCategoryDto(); + dto.setName("Test Category"); + + ReportCategory reportCategory1 = ReportCategory.newInstance(dto); + ReportCategory reportCategory2 = ReportCategory.newInstance(dto); + + assertThat(reportCategory1).isEqualTo(reportCategory2); + assertThat(reportCategory1.hashCode()).isEqualTo(reportCategory2.hashCode()); + } +} diff --git a/src/test/java/org/openlmis/report/service/JasperTemplateServiceTest.java b/src/test/java/org/openlmis/report/service/JasperTemplateServiceTest.java index 4f69c24..1ee0ab6 100644 --- a/src/test/java/org/openlmis/report/service/JasperTemplateServiceTest.java +++ b/src/test/java/org/openlmis/report/service/JasperTemplateServiceTest.java @@ -56,6 +56,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.UUID; import javax.imageio.ImageIO; @@ -76,6 +77,7 @@ import org.mockito.Mock; import org.openlmis.report.domain.JasperTemplate; import org.openlmis.report.domain.JasperTemplateParameter; +import org.openlmis.report.domain.ReportCategory; import org.openlmis.report.domain.ReportImage; import org.openlmis.report.dto.external.referencedata.RightDto; import org.openlmis.report.exception.JasperReportViewException; @@ -83,6 +85,7 @@ import org.openlmis.report.exception.ValidationMessageException; import org.openlmis.report.i18n.ReportImageMessageKeys; import org.openlmis.report.repository.JasperTemplateRepository; +import org.openlmis.report.repository.ReportCategoryRepository; import org.openlmis.report.repository.ReportImageRepository; import org.openlmis.report.service.referencedata.RightReferenceDataService; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -106,6 +109,9 @@ public class JasperTemplateServiceTest { @Mock private ReportImageRepository reportImageRepository; + @Mock + private ReportCategoryRepository reportCategoryRepository; + @InjectMocks private JasperTemplateService jasperTemplateService; @@ -114,6 +120,7 @@ public class JasperTemplateServiceTest { private static final String NAME_OF_FILE = "report.jrxml"; private static final String DISPLAY_NAME = "displayName"; + private static final String CATEGORY_NAME = "categoryName"; private static final String PARAM_DISPLAY_NAME = "Param Display Name"; private static final String REQUIRED = "required"; private static final String PARAM1 = "param1"; @@ -124,7 +131,7 @@ public class JasperTemplateServiceTest { private HttpServletRequest request; private JasperTemplate template; - + @Before public void setUp() { request = mock(HttpServletRequest.class); @@ -133,35 +140,50 @@ public void setUp() { @Test public void shouldSaveValidNonExistentTemplate() throws Exception { + ReportCategory reportCategory = new ReportCategory(); + reportCategory.setId(UUID.randomUUID()); + reportCategory.setName(CATEGORY_NAME); + // given given(jasperTemplateRepository.findByName(anyString())) .willReturn(null); + given(reportCategoryRepository.findByName(anyString())) + .willReturn(Optional.of(reportCategory)); + // when - testSaveTemplate(DISPLAY_NAME); + testSaveTemplate(); } @Test public void shouldUpdateValidExistentTemplate() throws Exception { // given + ReportCategory reportCategory = new ReportCategory(); + reportCategory.setId(UUID.randomUUID()); + reportCategory.setName(CATEGORY_NAME); + UUID oldId = UUID.randomUUID(); JasperTemplate oldTemplate = new JasperTemplate(); oldTemplate.setName(DISPLAY_NAME); oldTemplate.setId(oldId); oldTemplate.setRequiredRights(new ArrayList<>()); + oldTemplate.setCategory(reportCategory); given(jasperTemplateRepository.findByName(anyString())) .willReturn(oldTemplate); + given(reportCategoryRepository.findByName(anyString())) + .willReturn(Optional.ofNullable(oldTemplate.getCategory())); + // when - JasperTemplate template = testSaveTemplate(DISPLAY_NAME); + JasperTemplate template = testSaveTemplate(); // then assertEquals(template.getId(), oldId); } - private JasperTemplate testSaveTemplate(String name) throws ReportingException { + private JasperTemplate testSaveTemplate() throws ReportingException { JasperTemplateService service = spy(jasperTemplateService); MultipartFile file = mock(MultipartFile.class); String description = "description"; @@ -175,10 +197,11 @@ private JasperTemplate testSaveTemplate(String name) throws ReportingException { .validateFileAndSaveTemplate(any(JasperTemplate.class), eq(file)); // when - JasperTemplate resultTemplate = service.saveTemplate(file, name, description, requiredRights); + JasperTemplate resultTemplate = service.saveTemplate(file, + JasperTemplateServiceTest.DISPLAY_NAME, description, requiredRights, CATEGORY_NAME); // then - assertEquals(name, resultTemplate.getName()); + assertEquals(JasperTemplateServiceTest.DISPLAY_NAME, resultTemplate.getName()); assertEquals(description, resultTemplate.getDescription()); assertEquals(requiredRights, resultTemplate.getRequiredRights()); @@ -195,7 +218,8 @@ public void shouldThrowErrorIfReportRequiredRightDoesNotExist() throws Exception given(rightReferenceDataService.findRight(rejectedRight)).willReturn(null); // when - jasperTemplateService.saveTemplate(null, null, null, Collections.singletonList(rejectedRight)); + jasperTemplateService.saveTemplate(null, null, null, Collections.singletonList(rejectedRight), + null); } @Test diff --git a/src/test/java/org/openlmis/report/utils/AuthenticationHelperTest.java b/src/test/java/org/openlmis/report/utils/AuthenticationHelperTest.java index c79f909..5850590 100644 --- a/src/test/java/org/openlmis/report/utils/AuthenticationHelperTest.java +++ b/src/test/java/org/openlmis/report/utils/AuthenticationHelperTest.java @@ -18,18 +18,19 @@ import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.UUID; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; import org.openlmis.report.dto.external.DtoGenerator; import org.openlmis.report.dto.external.referencedata.RightDto; import org.openlmis.report.dto.external.referencedata.UserDto; @@ -40,7 +41,7 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class AuthenticationHelperTest { @Mock @@ -52,10 +53,12 @@ public class AuthenticationHelperTest { @InjectMocks private AuthenticationHelper authenticationHelper; - private UUID userId = UUID.randomUUID(); + private final UUID userId = UUID.randomUUID(); @Before public void setUp() { + MockitoAnnotations.initMocks(this); + Authentication authentication = mock(Authentication.class); when(authentication.getPrincipal()).thenReturn(userId); diff --git a/src/test/java/org/openlmis/report/utils/DashboardReportDataBuilder.java b/src/test/java/org/openlmis/report/utils/DashboardReportDataBuilder.java new file mode 100644 index 0000000..9359565 --- /dev/null +++ b/src/test/java/org/openlmis/report/utils/DashboardReportDataBuilder.java @@ -0,0 +1,90 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.utils; + +import java.util.UUID; +import org.apache.commons.lang.RandomStringUtils; +import org.openlmis.report.domain.DashboardReport; +import org.openlmis.report.domain.ReportCategory; + +public class DashboardReportDataBuilder { + private final UUID id = UUID.randomUUID(); + private String name = RandomStringUtils.random(6); + private String url = "http://example.com"; + private ReportType type = ReportType.SUPERSET; + private boolean enabled = true; + private boolean showOnHomePage = false; + private ReportCategory reportCategory = new ReportCategoryDataBuilder().buildAsNew(); + private final String rightName = (this.name + "_RIGHT").toUpperCase(); + + public DashboardReportDataBuilder withName(String name) { + this.name = name; + return this; + } + + public DashboardReportDataBuilder withUrl(String url) { + this.url = url; + return this; + } + + public DashboardReportDataBuilder withType(ReportType type) { + this.type = type; + return this; + } + + public DashboardReportDataBuilder withEnabled(boolean enabled) { + this.enabled = enabled; + return this; + } + + public DashboardReportDataBuilder withShowOnHomePage(boolean showOnHomePage) { + this.showOnHomePage = showOnHomePage; + return this; + } + + public DashboardReportDataBuilder withCategory(ReportCategory reportCategory) { + this.reportCategory = reportCategory; + return this; + } + + /** + * Builds an instance of the {@link DashboardReport} class with populated ID. + * + * @return the instance of {@link DashboardReport} class + */ + public DashboardReport build() { + DashboardReport dashboardReport = buildAsNew(); + dashboardReport.setId(id); + return dashboardReport; + } + + /** + * Build an instance of the {@link DashboardReport} class without ID field populated. + * + * @return the instance of {@link DashboardReport} class + */ + public DashboardReport buildAsNew() { + return new DashboardReport( + name, + url, + type, + enabled, + showOnHomePage, + reportCategory, + rightName + ); + } +} \ No newline at end of file diff --git a/src/test/java/org/openlmis/report/utils/PaginationTest.java b/src/test/java/org/openlmis/report/utils/PaginationTest.java index d1bc692..1d1ea78 100644 --- a/src/test/java/org/openlmis/report/utils/PaginationTest.java +++ b/src/test/java/org/openlmis/report/utils/PaginationTest.java @@ -20,12 +20,12 @@ import java.util.ArrayList; import java.util.List; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.runners.MockitoJUnitRunner; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class PaginationTest { @Test @@ -89,20 +89,19 @@ public void getPageReturnsSomeValuesEvenWhenSizeIsOutOfBounds() { } private List getList() { - List values = new ArrayList() {{ - add(new Integer(0)); - add(new Integer(1)); - add(new Integer(2)); - add(new Integer(3)); - add(new Integer(4)); - add(new Integer(5)); - add(new Integer(6)); - add(new Integer(7)); - add(new Integer(8)); - add(new Integer(9)); + return new ArrayList() {{ + add(0); + add(1); + add(2); + add(3); + add(4); + add(5); + add(6); + add(7); + add(8); + add(9); } }; - return values; } } diff --git a/src/test/java/org/openlmis/report/utils/ReportCategoryDataBuilder.java b/src/test/java/org/openlmis/report/utils/ReportCategoryDataBuilder.java new file mode 100644 index 0000000..9ebe10f --- /dev/null +++ b/src/test/java/org/openlmis/report/utils/ReportCategoryDataBuilder.java @@ -0,0 +1,51 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.report.utils; + +import java.util.UUID; +import org.apache.commons.lang.RandomStringUtils; +import org.openlmis.report.domain.ReportCategory; + +public class ReportCategoryDataBuilder { + private final UUID id = UUID.randomUUID(); + private String name = RandomStringUtils.random(6); + + public ReportCategoryDataBuilder withName(String name) { + this.name = name; + return this; + } + + /** + * Builds an instance of the {@link ReportCategory} class with populated ID. + * + * @return the instance of {@link ReportCategory} class + */ + public ReportCategory build() { + ReportCategory reportCategory = buildAsNew(); + reportCategory.setId(id); + return reportCategory; + } + + /** + * Build an instance of the {@link ReportCategory} class without ID field populated. + * + * @return the instance of {@link ReportCategory} class + */ + public ReportCategory buildAsNew() { + return new ReportCategory(name); + } + +} \ No newline at end of file