diff --git a/src/main/java/com/faendir/acra/dataprovider/QueryDslDataProvider.java b/src/main/java/com/faendir/acra/dataprovider/QueryDslDataProvider.java index 55540af..9270ef1 100644 --- a/src/main/java/com/faendir/acra/dataprovider/QueryDslDataProvider.java +++ b/src/main/java/com/faendir/acra/dataprovider/QueryDslDataProvider.java @@ -1,8 +1,8 @@ package com.faendir.acra.dataprovider; +import com.querydsl.core.types.Expression; import com.querydsl.core.types.Order; import com.querydsl.core.types.OrderSpecifier; -import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.impl.JPAQuery; import com.vaadin.data.provider.AbstractBackEndDataProvider; import com.vaadin.data.provider.Query; @@ -10,7 +10,9 @@ import com.vaadin.data.provider.QuerySortOrder; import com.vaadin.shared.data.sort.SortDirection; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Stream; /** @@ -21,8 +23,9 @@ public class QueryDslDataProvider extends AbstractBackEndDataProvider sizeListeners; private final JPAQuery fetchBase; private final JPAQuery countBase; + private final Map> sortOptions; - public QueryDslDataProvider(JPAQuery base){ + public QueryDslDataProvider(JPAQuery base) { this(base, base); } @@ -30,6 +33,7 @@ public class QueryDslDataProvider extends AbstractBackEndDataProvider(); + sortOptions = new HashMap<>(); } @Override @@ -42,11 +46,20 @@ public class QueryDslDataProvider extends AbstractBackEndDataProvider expression) { + String id = String.valueOf(sortOptions.size()); + sortOptions.put(id, expression); + return id; + } + @Override protected Stream fetchFromBackEnd(Query query) { JPAQuery q = fetchBase.clone().offset(query.getOffset()).limit(query.getLimit()); - for (QuerySortOrder order : query.getSortOrders()){ - q = q.orderBy(new OrderSpecifier<>(order.getDirection() == SortDirection.ASCENDING ? Order.ASC : Order.DESC, Expressions.asComparable(order.getSorted()))); + for (QuerySortOrder order : query.getSortOrders()) { + Expression sort = sortOptions.get(order.getSorted()); + if (sort != null) { + q = q.orderBy(new OrderSpecifier<>(order.getDirection() == SortDirection.ASCENDING ? Order.ASC : Order.DESC, sort)); + } } return q.fetch().stream(); } diff --git a/src/main/java/com/faendir/acra/service/data/DataService.java b/src/main/java/com/faendir/acra/service/data/DataService.java index edbad37..2be4c9c 100644 --- a/src/main/java/com/faendir/acra/service/data/DataService.java +++ b/src/main/java/com/faendir/acra/service/data/DataService.java @@ -1,6 +1,5 @@ package com.faendir.acra.service.data; -import com.faendir.acra.dataprovider.ObservableDataProvider; import com.faendir.acra.dataprovider.QueryDslDataProvider; import com.faendir.acra.model.App; import com.faendir.acra.model.Attachment; @@ -86,7 +85,7 @@ public class DataService implements Serializable { } @NonNull - public ObservableDataProvider getAppProvider() { + public QueryDslDataProvider getAppProvider() { boolean isAdmin = SecurityUtils.hasRole(User.Role.ADMIN); Function, BooleanExpression> existenceFunction = isAdmin ? JPQLQuery::notExists : JPQLQuery::exists; BiFunction, Permission.Level, BooleanExpression> compareFunction = isAdmin ? EnumPath::lt : EnumPath::goe; @@ -98,23 +97,23 @@ public class DataService implements Serializable { } @NonNull - public ObservableDataProvider getBugProvider(@NonNull App app, boolean onlyNonSolved) { + public QueryDslDataProvider getBugProvider(@NonNull App app, boolean onlyNonSolved) { BooleanExpression where = BUG.app.eq(app).and(onlyNonSolved ? BUG.solved.eq(false) : Expressions.TRUE); return new QueryDslDataProvider<>(Queries.selectVBug(entityManager).where(where), new JPAQuery<>(entityManager).from(BUG).where(where)); } @NonNull - public ObservableDataProvider getReportProvider(@NonNull Bug bug) { + public QueryDslDataProvider getReportProvider(@NonNull Bug bug) { return new QueryDslDataProvider<>(new JPAQuery<>(entityManager).from(REPORT).where(REPORT.bug.eq(bug)).select(REPORT)); } @NonNull - public ObservableDataProvider getReportProvider(@NonNull App app) { + public QueryDslDataProvider getReportProvider(@NonNull App app) { return new QueryDslDataProvider<>(new JPAQuery<>(entityManager).from(REPORT).join(REPORT.bug).where(REPORT.bug.app.eq(app)).select(REPORT)); } @NonNull - public ObservableDataProvider getMappingProvider(@NonNull App app) { + public QueryDslDataProvider getMappingProvider(@NonNull App app) { return new QueryDslDataProvider<>(new JPAQuery<>(entityManager).from(MAPPING).where(MAPPING.app.eq(app)).select(MAPPING)); } diff --git a/src/main/java/com/faendir/acra/service/user/UserService.java b/src/main/java/com/faendir/acra/service/user/UserService.java index 99221dc..a167c6d 100644 --- a/src/main/java/com/faendir/acra/service/user/UserService.java +++ b/src/main/java/com/faendir/acra/service/user/UserService.java @@ -1,7 +1,6 @@ package com.faendir.acra.service.user; import com.faendir.acra.config.AcraConfiguration; -import com.faendir.acra.dataprovider.ObservableDataProvider; import com.faendir.acra.dataprovider.QueryDslDataProvider; import com.faendir.acra.model.App; import com.faendir.acra.model.Permission; @@ -114,7 +113,7 @@ public class UserService implements Serializable { return new User(acraConfiguration.getUser().getName(), passwordEncoder.encode(acraConfiguration.getUser().getPassword()), Arrays.asList(User.Role.USER, User.Role.ADMIN)); } - public ObservableDataProvider getUserProvider() { + public QueryDslDataProvider getUserProvider() { return new QueryDslDataProvider<>(new JPAQuery<>(entityManager).from(USER).where(USER.roles.any().eq(User.Role.USER)).select(USER)); } } diff --git a/src/main/java/com/faendir/acra/ui/view/Overview.java b/src/main/java/com/faendir/acra/ui/view/Overview.java index 2977a69..c78464a 100644 --- a/src/main/java/com/faendir/acra/ui/view/Overview.java +++ b/src/main/java/com/faendir/acra/ui/view/Overview.java @@ -1,5 +1,7 @@ package com.faendir.acra.ui.view; +import com.faendir.acra.model.QApp; +import com.faendir.acra.model.QReport; import com.faendir.acra.model.User; import com.faendir.acra.model.view.VApp; import com.faendir.acra.security.SecurityUtils; @@ -44,8 +46,8 @@ public class Overview extends BaseView { grid.setResponsive(true); grid.setSizeToRows(); grid.setSelectionMode(Grid.SelectionMode.NONE); - grid.addColumn(app -> app.getApp().getName(), "name", "Name"); - grid.addColumn(VApp::getReportCount, "reportCount", "Reports"); + grid.addColumn(app -> app.getApp().getName(), QApp.app.name, "Name"); + grid.addColumn(VApp::getReportCount, QReport.report.count(), "Reports"); grid.addOnClickNavigation(getNavigationManager(), AppView.class, e -> String.valueOf(e.getItem().getApp().getId())); VerticalLayout layout = new VerticalLayout(); if (SecurityUtils.hasRole(User.Role.ADMIN)) { diff --git a/src/main/java/com/faendir/acra/ui/view/app/tabs/AdminTab.java b/src/main/java/com/faendir/acra/ui/view/app/tabs/AdminTab.java index 1eb3f1a..8427ddf 100644 --- a/src/main/java/com/faendir/acra/ui/view/app/tabs/AdminTab.java +++ b/src/main/java/com/faendir/acra/ui/view/app/tabs/AdminTab.java @@ -3,6 +3,7 @@ package com.faendir.acra.ui.view.app.tabs; import com.faendir.acra.model.App; import com.faendir.acra.model.Permission; import com.faendir.acra.model.ProguardMapping; +import com.faendir.acra.model.QProguardMapping; import com.faendir.acra.model.QReport; import com.faendir.acra.security.SecurityUtils; import com.faendir.acra.service.data.DataService; @@ -18,6 +19,7 @@ import com.faendir.acra.ui.view.base.ValidatedField; import com.faendir.acra.util.Style; import com.vaadin.icons.VaadinIcons; import com.vaadin.server.Sizeable; +import com.vaadin.shared.data.sort.SortDirection; import com.vaadin.shared.ui.ContentMode; import com.vaadin.spring.annotation.SpringComponent; import com.vaadin.spring.annotation.ViewScope; @@ -132,12 +134,12 @@ public class AdminTab implements AppTab { VerticalLayout layout = new VerticalLayout(); MyGrid grid = new MyGrid<>(null, dataService.getMappingProvider(app)); grid.setSizeToRows(); - grid.addColumn(ProguardMapping::getVersionCode, "Version"); + grid.sort(grid.addColumn(ProguardMapping::getVersionCode, QProguardMapping.proguardMapping.versionCode, "Version"), SortDirection.ASCENDING); if (SecurityUtils.hasPermission(app, Permission.Level.EDIT)) { grid.addColumn(report -> "Delete", new ButtonRenderer<>(e -> new Popup().setTitle("Confirm") .addComponent(new Label("Are you sure you want to delete the mapping for version " + e.getItem().getVersionCode() + "?")) - .addYesNoButtons(p -> dataService.delete(e.getItem()), true))).setSortable(false); + .addYesNoButtons(p -> dataService.delete(e.getItem()), true))); } layout.addComponent(grid); Style.NO_PADDING.apply(layout); diff --git a/src/main/java/com/faendir/acra/ui/view/app/tabs/BugTab.java b/src/main/java/com/faendir/acra/ui/view/app/tabs/BugTab.java index efe2848..4d455fe 100644 --- a/src/main/java/com/faendir/acra/ui/view/app/tabs/BugTab.java +++ b/src/main/java/com/faendir/acra/ui/view/app/tabs/BugTab.java @@ -2,6 +2,8 @@ package com.faendir.acra.ui.view.app.tabs; import com.faendir.acra.model.App; import com.faendir.acra.model.Permission; +import com.faendir.acra.model.QBug; +import com.faendir.acra.model.QReport; import com.faendir.acra.model.view.VBug; import com.faendir.acra.security.SecurityUtils; import com.faendir.acra.service.data.DataService; @@ -84,13 +86,13 @@ public class BugTab implements AppTab { header.addComponent(merge); header.addComponent(hideSolved); header.setComponentAlignment(hideSolved, Alignment.MIDDLE_RIGHT); - bugs.addColumn(VBug::getReportCount,"reportCount", "Reports"); - bugs.sort(bugs.addColumn(VBug::getLastReport, new TimeSpanRenderer(), "lastReport", "Latest Report"), SortDirection.DESCENDING); - bugs.addColumn(bug -> bug.getBug().getVersionCode(), "versionCode", "Version"); - bugs.addColumn(bug -> bug.getBug().getTitle(), "title", "Title").setExpandRatio(1).setMinimumWidthFromContent(false); + bugs.addColumn(VBug::getReportCount,QReport.report.count(), "Reports"); + bugs.sort(bugs.addColumn(VBug::getLastReport, new TimeSpanRenderer(), QReport.report.date.max(), "Latest Report"), SortDirection.DESCENDING); + bugs.addColumn(bug -> bug.getBug().getVersionCode(), QBug.bug.versionCode, "Version"); + bugs.addColumn(bug -> bug.getBug().getTitle(), QBug.bug.title, "Title").setExpandRatio(1).setMinimumWidthFromContent(false); bugs.addOnClickNavigation(navigationManager, com.faendir.acra.ui.view.bug.BugView.class, bugItemClick -> String.valueOf(bugItemClick.getItem().getBug().getId())); bugs.addColumn(bug -> new MyCheckBox(bug.getBug().isSolved(), SecurityUtils.hasPermission(app, Permission.Level.EDIT), e -> dataService.setBugSolved(bug.getBug(), e.getValue())), - new ComponentRenderer(), "solved", "Solved"); + new ComponentRenderer(), QBug.bug.solved, "Solved"); layout.addComponent(bugs); layout.setExpandRatio(bugs, 1); layout.setSizeFull(); diff --git a/src/main/java/com/faendir/acra/ui/view/base/MyGrid.java b/src/main/java/com/faendir/acra/ui/view/base/MyGrid.java index 57af434..4dc5525 100644 --- a/src/main/java/com/faendir/acra/ui/view/base/MyGrid.java +++ b/src/main/java/com/faendir/acra/ui/view/base/MyGrid.java @@ -1,10 +1,14 @@ package com.faendir.acra.ui.view.base; import com.faendir.acra.client.mygrid.MiddleClickGridExtensionConnector; -import com.faendir.acra.dataprovider.ObservableDataProvider; +import com.faendir.acra.dataprovider.QueryDslDataProvider; import com.faendir.acra.ui.navigation.NavigationManager; +import com.querydsl.core.types.Expression; import com.vaadin.data.ValueProvider; +import com.vaadin.data.provider.DataProvider; import com.vaadin.shared.MouseEventDetails; +import com.vaadin.shared.data.sort.SortDirection; +import com.vaadin.ui.Composite; import com.vaadin.ui.Grid; import com.vaadin.ui.renderers.AbstractRenderer; import com.vaadin.ui.renderers.TextRenderer; @@ -12,15 +16,21 @@ import elemental.json.JsonObject; import org.springframework.lang.NonNull; import java.util.Collection; +import java.util.Set; import java.util.function.Function; /** * @author Lukas * @since 14.05.2017 */ -public class MyGrid extends Grid { - public MyGrid(String caption, ObservableDataProvider dataProvider) { - super(caption, dataProvider); +public class MyGrid extends Composite { + private final ExposingGrid grid; + private QueryDslDataProvider dataProvider; + + public MyGrid(String caption, QueryDslDataProvider dataProvider) { + this.dataProvider = dataProvider; + grid = new ExposingGrid<>(caption, dataProvider); + setCompositionRoot(grid); setSizeFull(); MiddleClickExtension.extend(this); } @@ -32,48 +42,84 @@ public class MyGrid extends Grid { @NonNull public Grid.Column addColumn(@NonNull ValueProvider valueProvider, @NonNull AbstractRenderer renderer, @NonNull String caption) { - return addColumn(valueProvider, renderer).setId(caption).setCaption(caption).setSortable(false); + return grid.addColumn(valueProvider, renderer).setCaption(caption).setSortable(false); } @NonNull - public Grid.Column addColumn(@NonNull ValueProvider valueProvider, @NonNull String id, @NonNull String caption) { - return addColumn(valueProvider, new TextRenderer(), id, caption); + public Grid.Column addColumn(@NonNull ValueProvider valueProvider, @NonNull Expression sort, @NonNull String caption) { + return addColumn(valueProvider, new TextRenderer(), sort, caption); } @NonNull - public Grid.Column addColumn(@NonNull ValueProvider valueProvider, @NonNull AbstractRenderer renderer, @NonNull String id, - @NonNull String caption) { - return addColumn(valueProvider, renderer).setId(id).setCaption(caption); + public Grid.Column addColumn(@NonNull ValueProvider valueProvider, @NonNull AbstractRenderer renderer, + @NonNull Expression sort, @NonNull String caption) { + return grid.addColumn(valueProvider, renderer).setId(dataProvider.addSortable(sort)).setCaption(caption); + } + + @NonNull + public Grid.Column addColumn(@NonNull ValueProvider valueProvider, @NonNull AbstractRenderer renderer) { + return grid.addColumn(valueProvider, renderer).setSortable(false); } - @Override public void setItems(@NonNull Collection items) { - super.setItems(items); + grid.setItems(items); setHeightByRows(items.size()); } - @Override public void setHeightByRows(double rows) { - super.setHeightByRows(rows >= 1 ? rows : 1); + grid.setHeightByRows(rows >= 1 ? rows : 1); } public void setSizeToRows() { - ((ObservableDataProvider) getDataProvider()).addSizeListener(rows -> getUI().access(() -> setHeightByRows(rows))); + dataProvider.addSizeListener(rows -> getUI().access(() -> setHeightByRows(rows))); } - public void addOnClickNavigation(@NonNull NavigationManager navigationManager, Class namedView, Function, String> parameterGetter) { - addItemClickListener(e -> { + public void setBodyRowHeight(double rowHeight) { + grid.setBodyRowHeight(rowHeight); + } + + public void setSelectionMode(Grid.SelectionMode selectionMode) { + grid.setSelectionMode(selectionMode); + } + + public void sort(Grid.Column column, SortDirection direction) { + grid.sort(column, direction); + } + + public Set getSelectedItems() { + return grid.getSelectedItems(); + } + + public void select(T item) { + grid.select(item); + } + + public void addOnClickNavigation(@NonNull NavigationManager navigationManager, Class namedView, + Function, String> parameterGetter) { + grid.addItemClickListener(e -> { boolean newTab = e.getMouseEventDetails().getButton() == MouseEventDetails.MouseButton.MIDDLE || e.getMouseEventDetails().isCtrlKey(); navigationManager.navigateTo(namedView, parameterGetter.apply(e), newTab); }); } - public static class MiddleClickExtension extends AbstractGridExtension { - private MiddleClickExtension(MyGrid grid) { + public QueryDslDataProvider getDataProvider() { + return dataProvider; + } + + public void setDataProvider(QueryDslDataProvider dataProvider) { + this.dataProvider = dataProvider; + grid.setDataProvider(dataProvider); + } + + public static class MiddleClickExtension extends Grid.AbstractGridExtension { + private MiddleClickExtension(MyGrid myGrid) { + ExposingGrid grid = myGrid.grid; super.extend(grid); - registerRpc((rowIndex, rowKey, columnInternalId, details) -> grid.fireEvent( - new ItemClick<>(grid, grid.getColumnByInternalId(columnInternalId), grid.getDataCommunicator().getKeyMapper().get(rowKey), - details, rowIndex)), MiddleClickGridExtensionConnector.Rpc.class); + registerRpc((rowIndex, rowKey, columnInternalId, details) -> myGrid.fireEvent(new Grid.ItemClick<>(grid, + grid.getColumnByInternalId(columnInternalId), + grid.getDataCommunicator().getKeyMapper().get(rowKey), + details, + rowIndex)), MiddleClickGridExtensionConnector.Rpc.class); } public static void extend(MyGrid grid) { @@ -96,4 +142,15 @@ public class MyGrid extends Grid { public void refreshData(Object item) { } } + + private static class ExposingGrid extends Grid { + ExposingGrid(String caption, DataProvider dataProvider) { + super(caption, dataProvider); + } + + @Override + public Column getColumnByInternalId(String columnId) { + return super.getColumnByInternalId(columnId); + } + } } diff --git a/src/main/java/com/faendir/acra/ui/view/base/ReportList.java b/src/main/java/com/faendir/acra/ui/view/base/ReportList.java index 9afcfa0..473d72f 100644 --- a/src/main/java/com/faendir/acra/ui/view/base/ReportList.java +++ b/src/main/java/com/faendir/acra/ui/view/base/ReportList.java @@ -1,14 +1,16 @@ package com.faendir.acra.ui.view.base; -import com.faendir.acra.dataprovider.ObservableDataProvider; -import com.faendir.acra.security.SecurityUtils; +import com.faendir.acra.dataprovider.QueryDslDataProvider; import com.faendir.acra.model.App; import com.faendir.acra.model.Permission; +import com.faendir.acra.model.QReport; import com.faendir.acra.model.Report; +import com.faendir.acra.security.SecurityUtils; import com.faendir.acra.ui.navigation.NavigationManager; import com.faendir.acra.ui.view.report.ReportView; import com.faendir.acra.util.TimeSpanRenderer; import com.vaadin.shared.data.sort.SortDirection; +import com.vaadin.ui.Grid; import com.vaadin.ui.Label; import com.vaadin.ui.renderers.ButtonRenderer; import org.springframework.lang.NonNull; @@ -22,15 +24,15 @@ import java.util.function.Consumer; public class ReportList extends MyGrid { public static final String CAPTION = "Reports"; - public ReportList(App app, @NonNull NavigationManager navigationManager, @NonNull Consumer reportDeleter, @NonNull ObservableDataProvider reportProvider) { + public ReportList(App app, @NonNull NavigationManager navigationManager, @NonNull Consumer reportDeleter, @NonNull QueryDslDataProvider reportProvider) { super(CAPTION, reportProvider); setId(CAPTION); - setSelectionMode(SelectionMode.NONE); - sort(addColumn(Report::getDate, new TimeSpanRenderer(), "date", "Date"), SortDirection.DESCENDING); - addColumn(Report::getVersionCode, "versionCode", "App Version"); - addColumn(Report::getAndroidVersion, "androidVersion", "Android Version"); - addColumn(Report::getPhoneModel, "phoneModel", "Device"); - addColumn(report -> report.getStacktrace().split("\n", 2)[0], "stacktrace", "Stacktrace").setExpandRatio(1).setMinimumWidthFromContent(false); + setSelectionMode(Grid.SelectionMode.NONE); + sort(addColumn(Report::getDate, new TimeSpanRenderer(), QReport.report.date, "Date"), SortDirection.DESCENDING); + addColumn(Report::getVersionCode, QReport.report.versionCode, "App Version"); + addColumn(Report::getAndroidVersion, QReport.report.androidVersion, "Android Version"); + addColumn(Report::getPhoneModel, QReport.report.phoneModel, "Device"); + addColumn(report -> report.getStacktrace().split("\n", 2)[0], QReport.report.stacktrace, "Stacktrace").setExpandRatio(1).setMinimumWidthFromContent(false); if (SecurityUtils.hasPermission(app, Permission.Level.EDIT)) { addColumn(report -> "Delete", new ButtonRenderer<>(e -> new Popup().setTitle("Confirm") .addComponent(new Label("Are you sure you want to delete this report?")) diff --git a/src/main/java/com/faendir/acra/ui/view/user/UserManagerView.java b/src/main/java/com/faendir/acra/ui/view/user/UserManagerView.java index f0c7824..6270c7c 100644 --- a/src/main/java/com/faendir/acra/ui/view/user/UserManagerView.java +++ b/src/main/java/com/faendir/acra/ui/view/user/UserManagerView.java @@ -2,6 +2,7 @@ package com.faendir.acra.ui.view.user; import com.faendir.acra.model.App; import com.faendir.acra.model.Permission; +import com.faendir.acra.model.QUser; import com.faendir.acra.model.User; import com.faendir.acra.security.SecurityUtils; import com.faendir.acra.service.data.DataService; @@ -54,7 +55,7 @@ public class UserManagerView extends BaseView { userGrid.setSelectionMode(Grid.SelectionMode.NONE); userGrid.setBodyRowHeight(42); userGrid.setSizeToRows(); - userGrid.addColumn(User::getUsername, "username", "Username"); + userGrid.addColumn(User::getUsername, QUser.user.username, "Username"); userGrid.addColumn(user -> new MyCheckBox(user.getRoles().contains(User.Role.ADMIN), !user.getUsername().equals(SecurityUtils.getUsername()), e -> { userService.setAdmin(user, e.getValue()); userGrid.getDataProvider().refreshAll();