diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/ReportResource.java b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/ReportResource.java index 93d20289e..58122f184 100644 --- a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/ReportResource.java +++ b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/ReportResource.java @@ -106,10 +106,10 @@ public class ReportResource { } JsonArray result = new JsonArray(); - try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, realm, getContext())) { - tx.getPrivilegeContext().validateAction(new SimpleRestrictable(ReportSearch.class.getName(), id)); + try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, realm, getContext()); + Report report = new Report(tx, id)) { - Report report = new Report(tx, id); + tx.getPrivilegeContext().validateAction(new SimpleRestrictable(ReportSearch.class.getName(), id)); // set i18n data if possible if (localeJ != null) @@ -160,11 +160,10 @@ public class ReportResource { localeJ = localesJ.get(cert.getLocale().toString()).getAsJsonObject(); } - try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, realm, getContext())) { - tx.getPrivilegeContext().validateAction(new SimpleRestrictable(ReportSearch.class.getName(), id)); + try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, realm, getContext()); + Report report = new Report(tx, id)) { - // get report - Report report = new Report(tx, id); + tx.getPrivilegeContext().validateAction(new SimpleRestrictable(ReportSearch.class.getName(), id)); // set i18n data if possible if (localeJ != null) @@ -260,14 +259,13 @@ public class ReportResource { localeJ = localesJ.get(cert.getLocale().toString()).getAsJsonObject(); } - try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, realm, getContext())) { + long start = System.nanoTime(); + + try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, realm, getContext()); + Report report = new Report(tx, id)) { + tx.getPrivilegeContext().validateAction(new SimpleRestrictable(ReportSearch.class.getName(), id)); - long start = System.nanoTime(); - - // get report - Report report = new Report(tx, id); - // set i18n data if possible if (localeJ != null) report.getReportPolicy().setI18nData(localeJ); @@ -354,54 +352,56 @@ public class ReportResource { localeJ = localesJ.get(cert.getLocale().toString()).getAsJsonObject(); } - try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, realm, getContext())) { - tx.getPrivilegeContext().validateAction(new SimpleRestrictable(ReportSearch.class.getName(), id)); + // create CSV printer with header + StreamingOutput out = getOut(cert, realm, id, localeJ, filters); - // get report - Report report = new Report(tx, id); - - // set i18n data if possible - if (localeJ != null) - report.getReportPolicy().setI18nData(localeJ); - - // add filters from request - filters.keySet().forEach(f -> report.filter(f, filters.getSet(f))); - - // get headers - List orderedColumnKeys = report.getColumnKeys(); - String[] headers = new String[orderedColumnKeys.size()]; - orderedColumnKeys.toArray(headers); - - if (localeJ != null) { - for (int i = 0; i < headers.length; i++) { - if (localeJ.has(headers[i])) - headers[i] = localeJ.get(headers[i]).getAsString(); - } - } - - // create CSV printer with header - StreamingOutput out = getOut(report, headers); - - // send - String fileName = id + "_" + System.currentTimeMillis() + ".csv"; - return Response.ok(out, TEXT_CSV_TYPE) - .header("Content-Disposition", "attachment; filename=\"" + fileName + "\"").build(); - } + // send + String fileName = id + "_" + System.currentTimeMillis() + ".csv"; + return Response.ok(out, TEXT_CSV_TYPE) + .header("Content-Disposition", "attachment; filename=\"" + fileName + "\"").build(); } - private StreamingOutput getOut(Report report, String[] headers) { + private StreamingOutput getOut(Certificate cert, String realm, String reportId, JsonObject localeJ, + MapOfSets filters) { + return out -> { - // get report content and add to the buffer - try (CSVPrinter csvP = new CSVPrinter(new OutputStreamWriter(out), - CSVFormat.DEFAULT.withHeader(headers).withDelimiter(';'))) { - report.doReport().forEach(row -> { - try { - csvP.printRecord(row.valueStream().collect(Collectors.toList())); // add to CSV - } catch (Exception e) { - logger.error("Could not write CSV row", e); + try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, realm, getContext()); + Report report = new Report(tx, reportId)) { + + tx.getPrivilegeContext().validateAction(new SimpleRestrictable(ReportSearch.class.getName(), reportId)); + + // set i18n data if possible + if (localeJ != null) + report.getReportPolicy().setI18nData(localeJ); + + // add filters from request + filters.keySet().forEach(f -> report.filter(f, filters.getSet(f))); + + // get headers + List orderedColumnKeys = report.getColumnKeys(); + String[] headers = new String[orderedColumnKeys.size()]; + orderedColumnKeys.toArray(headers); + + if (localeJ != null) { + for (int i = 0; i < headers.length; i++) { + if (localeJ.has(headers[i])) + headers[i] = localeJ.get(headers[i]).getAsString(); } - }); + } + + // get report content and add to the buffer + try (CSVPrinter csvP = new CSVPrinter(new OutputStreamWriter(out), + CSVFormat.DEFAULT.withHeader(headers).withDelimiter(';'))) { + + report.doReport().forEach(row -> { + try { + csvP.printRecord(row.valueStream().collect(Collectors.toList())); // add to CSV + } catch (Exception e) { + logger.error("Could not write CSV row", e); + } + }); + } } }; } diff --git a/li.strolch.service/src/main/java/li/strolch/report/Report.java b/li.strolch.service/src/main/java/li/strolch/report/Report.java index 6d6e926c2..873c22e3e 100644 --- a/li.strolch.service/src/main/java/li/strolch/report/Report.java +++ b/li.strolch.service/src/main/java/li/strolch/report/Report.java @@ -17,7 +17,7 @@ import li.strolch.report.policy.ReportPolicy; import li.strolch.utils.collections.DateRange; import li.strolch.utils.collections.MapOfSets; -public class Report { +public class Report implements AutoCloseable { private final ReportPolicy reportPolicy; @@ -88,4 +88,12 @@ public class Report { }); } + @Override + public void close() { + try { + this.reportPolicy.close(); + } catch (Exception e) { + throw new IllegalStateException("Failed to close underlying policy " + this.reportPolicy.getClass(), e); + } + } } diff --git a/li.strolch.service/src/main/java/li/strolch/report/policy/GenericReport.java b/li.strolch.service/src/main/java/li/strolch/report/policy/GenericReport.java index 34c1f4220..b2fdb4537 100644 --- a/li.strolch.service/src/main/java/li/strolch/report/policy/GenericReport.java +++ b/li.strolch.service/src/main/java/li/strolch/report/policy/GenericReport.java @@ -129,6 +129,10 @@ public class GenericReport extends ReportPolicy { } } + public boolean isDescending() { + return this.descending; + } + public void setI18nData(JsonObject i18nData) { this.i18nData = i18nData; } diff --git a/li.strolch.service/src/main/java/li/strolch/report/policy/ReportPolicy.java b/li.strolch.service/src/main/java/li/strolch/report/policy/ReportPolicy.java index a40a216b9..5c6b8dfac 100644 --- a/li.strolch.service/src/main/java/li/strolch/report/policy/ReportPolicy.java +++ b/li.strolch.service/src/main/java/li/strolch/report/policy/ReportPolicy.java @@ -44,6 +44,10 @@ public abstract class ReportPolicy extends StrolchPolicy { public abstract long getCounter(); + public void close() throws Exception { + // do nothing + } + @Override public void undo() { // can't be undone diff --git a/li.strolch.service/src/test/java/li/strolch/report/GenericReportTest.java b/li.strolch.service/src/test/java/li/strolch/report/GenericReportTest.java index 9685dc282..051478c7d 100644 --- a/li.strolch.service/src/test/java/li/strolch/report/GenericReportTest.java +++ b/li.strolch.service/src/test/java/li/strolch/report/GenericReportTest.java @@ -46,10 +46,10 @@ public class GenericReportTest { @Test public void shouldGenerateJsonReport() { - try (StrolchTransaction tx = runtimeMock.openUserTx(certificate)) { + try (StrolchTransaction tx = runtimeMock.openUserTx(certificate); + Report report = new Report(tx, "stockReport")) { - new Report(tx, "stockReport") // - .doReportAsJson() // + report.doReportAsJson() // .forEach(e -> { switch (e.get("slot").getAsString()) { @@ -111,10 +111,10 @@ public class GenericReportTest { @Test public void shouldFilterReport1() { - try (StrolchTransaction tx = runtimeMock.openUserTx(certificate)) { + try (StrolchTransaction tx = runtimeMock.openUserTx(certificate); + Report report = new Report(tx, "stockReport")) { - new Report(tx, "stockReport") // - .filter("Product", "product01") // + report.filter("Product", "product01") // .doReportAsJson() // .forEach(e -> { @@ -134,10 +134,10 @@ public class GenericReportTest { @Test public void shouldFilterReport2() { - try (StrolchTransaction tx = runtimeMock.openUserTx(certificate)) { + try (StrolchTransaction tx = runtimeMock.openUserTx(certificate); + Report report = new Report(tx, "stockReport")) { - new Report(tx, "stockReport") // - .filter("Product", "product01") // + report.filter("Product", "product01") // .filter("Location", "location02") // .doReportAsJson() // .forEach(e -> { @@ -157,15 +157,15 @@ public class GenericReportTest { @Test public void shouldFilterReportByDateRange1() { - try (StrolchTransaction tx = runtimeMock.openUserTx(certificate)) { + try (StrolchTransaction tx = runtimeMock.openUserTx(certificate); + Report report = new Report(tx, "stockReport")) { Date from = new Date(LocalDate.of(2016, 1, 1).toEpochDay() * 86400000); Date to = new Date(LocalDate.of(2017, 1, 1).toEpochDay() * 86400000); DateRange dateRange = new DateRange().from(from, true).to(to, false); // expect no slots as all not in date range - List result = new Report(tx, "stockReport") // - .filter("Product", "product01") // + List result = report.filter("Product", "product01") // .dateRange(dateRange) // .doReportAsJson() // .collect(toList()); @@ -189,38 +189,48 @@ public class GenericReportTest { try (StrolchTransaction tx = runtimeMock.openUserTx(certificate)) { Date from = new Date(LocalDate.of(2016, 1, 1).toEpochDay() * 86400000); - Date to = new Date(LocalDate.of(2017, 1, 1).toEpochDay() * 86400000); - DateRange dateRange = new DateRange().from(from, true).to(to, false); // expect no orders as all not in date range - Report report = new Report(tx, "fromStockReport"); - List result = report.filter("Product", "product01").dateRange(dateRange).doReportAsJson() - .collect(toList()); - assertTrue(result.isEmpty()); + try (Report report = new Report(tx, "fromStockReport")) { + + Date to = new Date(LocalDate.of(2017, 1, 1).toEpochDay() * 86400000); + DateRange dateRange = new DateRange().from(from, true).to(to, false); + + List result = report.filter("Product", "product01").dateRange(dateRange).doReportAsJson() + .collect(toList()); + assertTrue(result.isEmpty()); + } // expect 2 orders, as in date range - to = new Date(LocalDate.of(2017, 3, 1).toEpochDay() * 86400000); - dateRange = new DateRange().from(from, true).to(to, false); - report = new Report(tx, "fromStockReport"); - result = report.filter("Product", "product01").dateRange(dateRange).doReportAsJson().collect(toList()); - assertEquals(2, result.size()); + try (Report report = new Report(tx, "fromStockReport")) { + + Date to = new Date(LocalDate.of(2017, 3, 1).toEpochDay() * 86400000); + DateRange dateRange = new DateRange().from(from, true).to(to, false); + + List result = report.filter("Product", "product01").dateRange(dateRange).doReportAsJson() + .collect(toList()); + assertEquals(2, result.size()); + } // expect 4 orders, as all in date range - to = new Date(LocalDate.of(2017, 3, 2).toEpochDay() * 86400000); - dateRange = new DateRange().from(from, true).to(to, false); - report = new Report(tx, "fromStockReport"); - result = report.filter("Product", "product01", "product02").dateRange(dateRange).doReportAsJson() - .collect(toList()); - assertEquals(4, result.size()); + try (Report report = new Report(tx, "fromStockReport")) { + + Date to = new Date(LocalDate.of(2017, 3, 2).toEpochDay() * 86400000); + DateRange dateRange = new DateRange().from(from, true).to(to, false); + + List result = report.filter("Product", "product01", "product02").dateRange(dateRange) + .doReportAsJson().collect(toList()); + assertEquals(4, result.size()); + } } } @Test public void shouldCreateFilterCriteria() { - try (StrolchTransaction tx = runtimeMock.openUserTx(certificate)) { + try (StrolchTransaction tx = runtimeMock.openUserTx(certificate); + Report report = new Report(tx, "stockReport")) { - Report report = new Report(tx, "stockReport"); MapOfSets filterCriteria = report.generateFilterCriteria(); assertFalse(filterCriteria.containsSet("Location")); @@ -237,9 +247,9 @@ public class GenericReportTest { @Test public void shouldCreateFilterCriteriaFiltered1() { - try (StrolchTransaction tx = runtimeMock.openUserTx(certificate)) { + try (StrolchTransaction tx = runtimeMock.openUserTx(certificate); + Report report = new Report(tx, "stockReport")) { - Report report = new Report(tx, "stockReport"); MapOfSets filterCriteria = report.filter("Product", "product01") .generateFilterCriteria(); @@ -264,13 +274,13 @@ public class GenericReportTest { @Test public void shouldCreateFilterCriteriaFiltered2() { - try (StrolchTransaction tx = runtimeMock.openUserTx(certificate)) { + try (StrolchTransaction tx = runtimeMock.openUserTx(certificate); + Report report = new Report(tx, "stockReport")) { Date from = new Date(LocalDate.of(2017, 3, 1).toEpochDay() * 86400000); Date to = new Date(LocalDate.of(2017, 3, 5).toEpochDay() * 86400000); DateRange dateRange = new DateRange().from(from, true).to(to, false); - Report report = new Report(tx, "stockReport"); MapOfSets filterCriteria = report // .dateRange(dateRange) // .filter("Product", "product01") //