mongod pre-delete

This commit is contained in:
F43nd1r 2017-12-12 01:13:48 +01:00
parent b368790358
commit 694d48b352
15 changed files with 212 additions and 102 deletions

View file

@ -8,10 +8,10 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
@ -31,7 +31,7 @@ public class MongoConfiguration {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoFactory);
MappingMongoConverter mongoConverter = new MappingMongoConverter(dbRefResolver, mongoMappingContext);
mongoConverter.setMapKeyDotReplacement("%&&%");
mongoConverter.setCustomConversions(new CustomConversions(Arrays.asList(new StacktraceElementReadConverter(), new StacktraceElementWriteConverter())));
mongoConverter.setCustomConversions(new MongoCustomConversions(Arrays.asList(new StacktraceElementReadConverter(), new StacktraceElementWriteConverter())));
mongoConverter.afterPropertiesSet();
return mongoConverter;
}

View file

@ -2,6 +2,8 @@ package com.faendir.acra.mongod.data;
import com.faendir.acra.mongod.model.Bug;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
@ -11,8 +13,13 @@ import java.util.List;
* @since 31.05.2017
*/
interface BugRepository extends MongoRepository<Bug, String> {
@NotNull
List<Bug> findByApp(String app);
@NotNull
List<Bug> findByReportIdsContains(String reportId);
@NotNull List<Bug> findByApp(String app);
@NotNull Page<Bug> findAllByApp(String app, Pageable pageable);
@NotNull Page<Bug> findAllByAppAndSolvedIsFalse(String app, Pageable pageable);
int countAllByApp(String app);
@NotNull List<Bug> findByReportIdsContains(String reportId);
}

View file

@ -6,8 +6,10 @@ import com.faendir.acra.mongod.model.ParsedException;
import com.faendir.acra.mongod.model.ProguardMapping;
import com.faendir.acra.mongod.model.Report;
import com.faendir.acra.mongod.model.ReportInfo;
import com.faendir.acra.mongod.util.BufferedMongoDataProvider;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.vaadin.data.provider.DataProvider;
import org.apache.commons.io.FileUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -15,12 +17,11 @@ import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.springframework.web.multipart.MultipartFile;
@ -35,9 +36,12 @@ import java.io.StringWriter;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -47,10 +51,6 @@ import java.util.stream.Stream;
*/
@Component
public class DataManager {
private static final String APP_REPORT_CACHE = "appReport";
private static final String APP_CACHE = "app";
private static final String BUG_REPORT_CACHE = "bugReport";
private static final String APP_BUG_CACHE = "appBug";
@NotNull private final MappingRepository mappingRepository;
@NotNull private final ReportRepository reportRepository;
@NotNull private final AppRepository appRepository;
@ -75,14 +75,12 @@ public class DataManager {
this.listenerExecutor = Executors.newSingleThreadExecutor();
}
@CacheEvict(value = APP_CACHE, allEntries = true)
public synchronized void createNewApp(@NotNull String name) {
byte[] bytes = new byte[12];
secureRandom.nextBytes(bytes);
appRepository.save(new App(name, Base64Utils.encodeToString(bytes)));
}
@Cacheable(value = APP_CACHE)
@NotNull
public List<App> getApps() {
return appRepository.findAll();
@ -90,19 +88,19 @@ public class DataManager {
@Nullable
public App getApp(@NotNull String id) {
return appRepository.findOne(id);
return appRepository.findById(id).orElse(null);
}
@CacheEvict(value = APP_CACHE, allEntries = true)
public synchronized void deleteApp(@NotNull String id) {
appRepository.delete(id);
appRepository.deleteById(id);
getReportsForApp(id).forEach(this::deleteReport);
mappingRepository.delete(getMappings(id));
mappingRepository.deleteAll(getMappings(id));
}
@NotNull
public List<GridFSDBFile> getAttachments(@NotNull String report) {
return gridFsTemplate.find(new Query(Criteria.where("metadata.reportId").is(report)));
public List<Pair<GridFSFile, Supplier<GridFsResource>>> getAttachments(@NotNull String report) {
return gridFsTemplate.find(new Query(Criteria.where("metadata.reportId").is(report)))
.map(file -> Pair.of(file, (Supplier<GridFsResource>) () -> gridFsTemplate.getResource(file.getFilename()))).into(new ArrayList<>());
}
public synchronized void addMapping(@NotNull String app, int version, @NotNull String mappings) {
@ -111,7 +109,7 @@ public class DataManager {
@Nullable
private ProguardMapping getMapping(@NotNull String app, int version) {
return mappingRepository.findOne(new ProguardMapping.MetaData(app, version));
return mappingRepository.findById(new ProguardMapping.MetaData(app, version)).orElse(null);
}
@NotNull
@ -123,7 +121,6 @@ public class DataManager {
newReport(app, content, Collections.emptyList());
}
@CacheEvict(value = APP_REPORT_CACHE, key = "#a0")
public synchronized void newReport(@NotNull String app, @NotNull JSONObject content, @NotNull List<MultipartFile> attachments) {
Report report = reportRepository.save(new Report(content, app));
for (MultipartFile a : attachments) {
@ -137,10 +134,10 @@ public class DataManager {
notifyListeners(info);
Bug bug = getBugs(app).stream().filter(b -> matches(b, info)).findAny().orElseGet(() -> new Bug(info.getApp(), info.getStacktrace(), info.getVersionCode()));
bug.getReportIds().add(info.getId());
bug.setLastReport(info.getDate());
saveBug(bug);
}
@Cacheable(APP_REPORT_CACHE)
@NotNull
public List<ReportInfo> getReportsForApp(@NotNull String app) {
try (Stream<Report> stream = reportRepository.streamAllByApp(app)) {
@ -148,18 +145,21 @@ public class DataManager {
}
}
public DataProvider<ReportInfo, Void> getLazyReportsForApp(@NotNull String app) {
return BufferedMongoDataProvider.of(pageable -> reportRepository.findAllByApp(app, pageable), ReportInfo::new);
}
public long reportCountForApp(@NotNull String app) {
return reportRepository.countByApp(app);
}
@Nullable
public Report getReport(@NotNull String id) {
return reportRepository.findOne(id);
return reportRepository.findById(id).orElse(null);
}
@CacheEvict(value = APP_REPORT_CACHE, key = "#a0.app")
public synchronized void deleteReport(@NotNull ReportInfo report) {
reportRepository.delete(report.getId());
reportRepository.deleteById(report.getId());
gridFsTemplate.delete(new Query(Criteria.where("metadata.reportId").is(report.getId())));
bugRepository.findByReportIdsContains(report.getId()).forEach(bug -> {
bug.getReportIds().remove(report.getId());
@ -173,48 +173,54 @@ public class DataManager {
}
@SuppressWarnings("WeakerAccess")
@CacheEvict(value = APP_BUG_CACHE, key = "#a0.app")
public void deleteBug(@NotNull Bug bug) {
bugRepository.delete(bug);
notifyListeners(bug);
}
@SuppressWarnings("WeakerAccess")
@Caching(evict = {@CacheEvict(value = APP_BUG_CACHE, key = "#a0.app"), @CacheEvict(value = BUG_REPORT_CACHE, key = "#a0.id")})
@NotNull
public Bug saveBug(@NotNull Bug bug) {
public void saveBug(@NotNull Bug bug) {
Bug b = bugRepository.save(bug);
notifyListeners(bug);
return b;
}
@NotNull
@Cacheable(value = APP_BUG_CACHE)
public List<Bug> getBugs(@NotNull String app) {
return bugRepository.findByApp(app);
}
@Cacheable(value = BUG_REPORT_CACHE, key = "#a0.id")
@NotNull
public List<ReportInfo> getReportsForBug(@NotNull Bug bug) {
try (Stream<Report> stream = reportRepository.streamAllByIdIn(bug.getReportIds())) {
return stream.map(ReportInfo::new).collect(Collectors.toList());
public DataProvider<Bug, Void> getLazyBugs(@NotNull String app, boolean includeSolved) {
if (includeSolved) {
return BufferedMongoDataProvider.of(pageable -> bugRepository.findAllByApp(app, pageable));
} else {
return BufferedMongoDataProvider.of(pageable -> bugRepository.findAllByAppAndSolvedIsFalse(app, pageable));
}
}
public DataProvider<ReportInfo, Void> getLazyReportsForBug(@NotNull Bug bug) {
return BufferedMongoDataProvider.of(pageable -> reportRepository.findAllByIdIn(bug.getReportIds(), pageable), ReportInfo::new);
}
public long reportCountForBug(@NotNull Bug bug) {
return bug.getReportIds().size();
}
public void rebuildBugs(@NotNull String app) {
getBugs(app).forEach(this::deleteBug);
List<Bug> bugs = new ArrayList<>();
getReportsForApp(app).forEach(reportInfo -> bugs.stream().filter(bug -> matches(bug, reportInfo)).findAny().orElseGet(() -> {
Bug bug = new Bug(reportInfo.getApp(), reportInfo.getStacktrace(), reportInfo.getVersionCode());
bugs.add(bug);
return bug;
}).getReportIds().add(reportInfo.getId()));
bugs.forEach(this::saveBug);
bugRepository.deleteAll();
Map<Bug, List<ReportInfo>> mapping = new HashMap<>();
getReportsForApp(app).forEach(reportInfo -> mapping.entrySet().stream().filter(entry -> matches(entry.getKey(), reportInfo)).map(Map.Entry::getValue).findAny()
.orElseGet(() -> {
List<ReportInfo> list = new ArrayList<>();
mapping.put(new Bug(reportInfo.getApp(), reportInfo.getStacktrace(), reportInfo.getVersionCode()), list);
return list;
})
.add(reportInfo));
mapping.forEach((bug, reports) -> {
reports.forEach(report->bug.getReportIds().add(report.getId()));
bug.setLastReport(ReportUtils.getLastReportDate(reports));
});
bugRepository.insert(mapping.keySet());
mapping.keySet().stream().findAny().ifPresent(this::notifyListeners);
}
public boolean matches(@NotNull Bug bug, @NotNull ReportInfo info) {
@ -275,5 +281,4 @@ public class DataManager {
}
}
}
}

View file

@ -2,6 +2,8 @@ package com.faendir.acra.mongod.data;
import com.faendir.acra.mongod.model.Report;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.stream.Stream;
@ -14,10 +16,16 @@ interface ReportRepository extends MongoRepository<Report, String> {
@NotNull
Stream<Report> streamAllByApp(String app);
long countByApp(String app);
int countByApp(String app);
@NotNull
Stream<Report> streamAllByIdIn(@NotNull Iterable<String> ids);
@NotNull
Page<Report> findAllByApp(String app, Pageable pageable);
@NotNull
Page<Report> findAllByIdIn(@NotNull Iterable<String> ids, Pageable pageable);
}

View file

@ -1,11 +1,13 @@
package com.faendir.acra.mongod.model;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
@ -14,25 +16,27 @@ import java.util.List;
*/
@Document
public class Bug implements AppScoped {
@NotNull private final String id;
@Nullable private final String id;
@NotNull @Indexed private final String app;
@NotNull private final String stacktrace;
private final int versionCode;
@NotNull private final List<String> reportIds;
private boolean solved;
@Nullable private Date lastReport;
@PersistenceConstructor
private Bug(@NotNull String id, @NotNull String app, boolean solved, @NotNull String stacktrace, int versionCode, @NotNull List<String> reportIds) {
private Bug(@Nullable String id, @NotNull String app, boolean solved, @NotNull String stacktrace, int versionCode, @NotNull List<String> reportIds, @Nullable Date lastReport) {
this.id = id;
this.app = app;
this.solved = solved;
this.stacktrace = stacktrace;
this.versionCode = versionCode;
this.reportIds = reportIds;
this.lastReport = lastReport;
}
public Bug(@NotNull String app, @NotNull String stacktrace, int versionCode){
this("", app, false, stacktrace, versionCode, new ArrayList<>());
this(null, app, false, stacktrace, versionCode, new ArrayList<>(), null);
}
public boolean isSolved() {
@ -63,8 +67,19 @@ public class Bug implements AppScoped {
return reportIds;
}
@NotNull
@Nullable
public String getId() {
return id;
}
@Nullable
public Date getLastReport() {
return lastReport;
}
public void setLastReport(@NotNull Date lastReport) {
if (this.lastReport == null || this.lastReport.before(lastReport)) {
this.lastReport = lastReport;
}
}
}

View file

@ -48,18 +48,16 @@ public class UserManager {
@Nullable
public User getUser(@NotNull String username) {
User user = userRepository.findOne(username);
if (user == null && defaultUser.equals(username)) {
user = getDefaultUser();
Optional<User> user = userRepository.findById(username);
if (!user.isPresent() && defaultUser.equals(username)) {
user = Optional.of(getDefaultUser());
}
if (user != null) {
ensureValidPermissions(user);
}
return user;
user.ifPresent(this::ensureValidPermissions);
return user.orElse(null);
}
public void createUser(@NotNull String username, @NotNull String password) {
if (userRepository.exists(username)) {
if (userRepository.existsById(username)) {
throw new IllegalArgumentException("Username already exists");
}
User user = new User(username, passwordEncoder.encode(password), Collections.singleton(ROLE_USER));

View file

@ -0,0 +1,64 @@
package com.faendir.acra.mongod.util;
import com.vaadin.data.provider.AbstractBackEndDataProvider;
import com.vaadin.data.provider.Query;
import com.vaadin.shared.data.sort.SortDirection;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author Lukas
* @since 07.12.2017
*/
public class BufferedMongoDataProvider<T,R> extends AbstractBackEndDataProvider<R, Void> {
private static final int PAGE_SIZE = 32;
private final Function<Pageable, Page<T>> getter;
private final Function<T, R> transformer;
public static <T, R> BufferedMongoDataProvider<T, R> of(Function<Pageable, Page<T>> getter, Function<T, R> transformer) {
return new BufferedMongoDataProvider<>(getter, transformer);
}
public static <R> BufferedMongoDataProvider<R,R> of(Function<Pageable, Page<R>> getter){
return of(getter, Function.identity());
}
private BufferedMongoDataProvider(Function<Pageable, Page<T>> getter, Function<T, R> transformer){
this.getter = getter;
this.transformer = transformer;
}
@Override
protected Stream<R> fetchFromBackEnd(Query<R, Void> query) {
Sort sort = Sort.by(query.getSortOrders().stream()
.map(s -> new Sort.Order(s.getDirection() == SortDirection.ASCENDING ? Sort.Direction.ASC : Sort.Direction.DESC, s.getSorted()))
.collect(Collectors.toList()));
Page<T> page = getter.apply(PageRequest.of(query.getOffset() / PAGE_SIZE, PAGE_SIZE, sort));
if (!page.hasContent()) return Stream.empty();
List<T> content = page.getContent();
int ignore = query.getOffset() % PAGE_SIZE;
int size = content.size() - ignore;
Stream<T> result = content.stream().skip(ignore);
while (size < query.getLimit() && page.hasNext()) {
page = getter.apply(page.nextPageable());
if (page.hasContent()) {
content = page.getContent();
size += content.size();
result = Stream.concat(result, content.stream());
}
}
return result.map(transformer);
}
@Override
protected int sizeInBackEnd(Query<R, Void> query) {
return (int) getter.apply(PageRequest.of(0, 1)).getTotalElements();
}
}

View file

@ -55,6 +55,7 @@ public class BackendUI extends UI {
} else {
showLogin();
}
//applicationContext.getBean(ToSqlMigrator.class).migrate();
}
private void login(@NotNull String username, @NotNull String password) {

View file

@ -6,7 +6,7 @@ import com.faendir.acra.mongod.model.Report;
import com.faendir.acra.ui.view.annotation.RequiresAppPermission;
import com.faendir.acra.ui.view.base.NamedView;
import com.faendir.acra.util.Style;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.server.FileDownloader;
import com.vaadin.server.StreamResource;
@ -23,12 +23,16 @@ import com.vaadin.ui.VerticalLayout;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.util.Pair;
import java.io.IOException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
@ -81,10 +85,16 @@ public class ReportView extends NamedView {
public void enter(@NotNull ViewChangeListener.ViewChangeEvent event) {
Report report = dataManager.getReport(event.getParameters());
assert report != null;
List<GridFSDBFile> attachmentList = dataManager.getAttachments(report.getId());
List<Pair<GridFSFile, Supplier<GridFsResource>>> attachmentList = dataManager.getAttachments(report.getId());
HorizontalLayout attachments = new HorizontalLayout(attachmentList.stream().map(file -> {
Button button = new Button(file.getFilename());
new FileDownloader(new StreamResource(file::getInputStream, file.getFilename())).extend(button);
Button button = new Button(file.getFirst().getFilename());
new FileDownloader(new StreamResource(() -> {
try {
return file.getSecond().get().getInputStream();
} catch (IOException e) {
return null;
}
}, file.getFirst().getFilename())).extend(button);
return button;
}).toArray(Component[]::new));
Style.apply(attachments, Style.MARGIN_BOTTOM, Style.MARGIN_TOP, Style.MARGIN_LEFT, Style.MARGIN_RIGHT);

View file

@ -1,6 +1,7 @@
package com.faendir.acra.ui.view.base;
import com.vaadin.data.ValueProvider;
import com.vaadin.data.provider.DataProvider;
import com.vaadin.ui.Grid;
import com.vaadin.ui.renderers.AbstractRenderer;
import com.vaadin.ui.renderers.TextRenderer;
@ -14,6 +15,10 @@ import java.util.Collection;
* @since 14.05.2017
*/
public class MyGrid<T> extends Grid<T> {
public <F> MyGrid(String caption, DataProvider<T, F> dataProvider) {
super(caption, dataProvider);
setSizeFull();
}
public MyGrid(@Nullable String caption, @NotNull Collection<T> items) {
super(caption, items);
@ -27,7 +32,18 @@ public class MyGrid<T> extends Grid<T> {
@NotNull
public <R> Grid.Column<T, R> addColumn(@NotNull ValueProvider<T, R> valueProvider, @NotNull AbstractRenderer<? super T, ? super R> renderer, @NotNull String caption) {
return addColumn(valueProvider, renderer).setId(caption).setCaption(caption);
return addColumn(valueProvider, renderer).setId(caption).setCaption(caption).setSortable(false);
}
@NotNull
public <R> Grid.Column<T, R> addColumn(@NotNull ValueProvider<T, R> valueProvider, @NotNull String id, @NotNull String caption) {
return addColumn(valueProvider, new TextRenderer(), id, caption);
}
@NotNull
public <R> Grid.Column<T, R> addColumn(@NotNull ValueProvider<T, R> valueProvider, @NotNull AbstractRenderer<? super T, ? super R> renderer, @NotNull String id,
@NotNull String caption) {
return addColumn(valueProvider, renderer).setId(id).setCaption(caption);
}
@Override

View file

@ -7,13 +7,12 @@ import com.faendir.acra.security.SecurityUtils;
import com.faendir.acra.ui.NavigationManager;
import com.faendir.acra.ui.view.ReportView;
import com.faendir.acra.util.TimeSpanRenderer;
import com.vaadin.data.provider.DataProvider;
import com.vaadin.shared.data.sort.SortDirection;
import com.vaadin.ui.renderers.ButtonRenderer;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* @author Lukas
@ -21,22 +20,22 @@ import java.util.function.Supplier;
*/
public class ReportList extends MyGrid<ReportInfo> implements DataManager.Listener<ReportInfo> {
public static final String CAPTION = "Reports";
@NotNull private final Supplier<List<ReportInfo>> reportSupplier;
@NotNull private final DataProvider<ReportInfo, Void> reportSupplier;
@NotNull private final Function<ReportInfo, Boolean> relevanceFunction;
public ReportList(String app, @NotNull NavigationManager navigationManager, @NotNull DataManager dataManager, @NotNull Supplier<List<ReportInfo>> reportSupplier,
public ReportList(String app, @NotNull NavigationManager navigationManager, @NotNull DataManager dataManager, @NotNull DataProvider<ReportInfo, Void> reportSupplier,
@NotNull Function<ReportInfo, Boolean> relevanceFunction) {
super(CAPTION, reportSupplier.get());
super(CAPTION, reportSupplier);
setId(CAPTION);
this.reportSupplier = reportSupplier;
this.relevanceFunction = relevanceFunction;
setWidth(100, Unit.PERCENTAGE);
setSelectionMode(SelectionMode.NONE);
sort(addColumn(ReportInfo::getDate, new TimeSpanRenderer(), "Date"), SortDirection.DESCENDING);
addColumn(ReportInfo::getVersionCode, "App Version");
addColumn(ReportInfo::getAndroidVersion, "Android Version");
addColumn(ReportInfo::getPhoneModel, "Device");
addColumn(report -> report.getStacktrace().split("\n", 2)[0], "Stacktrace").setExpandRatio(1);
sort(addColumn(ReportInfo::getDate, new TimeSpanRenderer(), "content.map.USER_CRASH_DATE", "Date"), SortDirection.DESCENDING);
addColumn(ReportInfo::getVersionCode, "content.map.APP_VERSION_CODE","App Version");
addColumn(ReportInfo::getAndroidVersion, "content.map.ANDROID_VERSION","Android Version");
addColumn(ReportInfo::getPhoneModel, "content.map.PHONE_MODEL","Device");
addColumn(report -> report.getStacktrace().split("\n", 2)[0], "content.map.STACK_TRACE","Stacktrace").setExpandRatio(1);
if (SecurityUtils.hasPermission(app, Permission.Level.EDIT)) {
addColumn(report -> "Delete", new ButtonRenderer<>(e -> dataManager.deleteReport(e.getItem())));
}
@ -53,6 +52,6 @@ public class ReportList extends MyGrid<ReportInfo> implements DataManager.Listen
}
private void setItems() {
getUI().access(() -> setItems(reportSupplier.get()));
getUI().access(reportSupplier::refreshAll);
}
}

View file

@ -1,7 +1,6 @@
package com.faendir.acra.ui.view.tabs;
import com.faendir.acra.mongod.data.DataManager;
import com.faendir.acra.mongod.data.ReportUtils;
import com.faendir.acra.mongod.model.AppScoped;
import com.faendir.acra.mongod.model.Bug;
import com.faendir.acra.mongod.model.Permission;
@ -17,13 +16,12 @@ import com.vaadin.shared.data.sort.SortDirection;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.renderers.ComponentRenderer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author Lukas
@ -46,15 +44,15 @@ public class BugTab extends VerticalLayout implements DataManager.Listener<AppSc
hideSolved.addValueChangeListener(e -> setItems());
addComponent(hideSolved);
setComponentAlignment(hideSolved, Alignment.MIDDLE_RIGHT);
bugs = new MyGrid<>(null, getBugs());
bugs = new MyGrid<>(null, dataManager.getLazyBugs(app, false));
bugs.setWidth(100, Unit.PERCENTAGE);
bugs.addColumn(dataManager::reportCountForBug, "Reports");
bugs.sort(bugs.addColumn(bug -> ReportUtils.getLastReportDate(dataManager.getReportsForBug(bug)), new TimeSpanRenderer(), "Latest Report"), SortDirection.DESCENDING);
bugs.addColumn(Bug::getVersionCode, "Version");
bugs.addColumn(bug -> bug.getStacktrace().split("\n", 2)[0], "Stacktrace").setExpandRatio(1);
bugs.addColumn(bug -> bug.getReportIds().size(), "Reports");
bugs.sort(bugs.addColumn(Bug::getLastReport, new TimeSpanRenderer(), "lastReport","Latest Report"), SortDirection.DESCENDING);
bugs.addColumn(Bug::getVersionCode, "versionCode","Version");
bugs.addColumn(bug -> bug.getStacktrace().split("\n", 2)[0], "stacktrace","Stacktrace").setExpandRatio(1);
bugs.addSelectionListener(this::handleBugSelection);
bugs.addComponentColumn(bug -> new MyCheckBox(bug.isSolved(), SecurityUtils.hasPermission(app, Permission.Level.EDIT), e -> dataManager.setBugSolved(bug, e.getValue())))
.setCaption("Solved");
bugs.addColumn(bug -> new MyCheckBox(bug.isSolved(), SecurityUtils.hasPermission(app, Permission.Level.EDIT), e -> dataManager.setBugSolved(bug, e.getValue())),
new ComponentRenderer(), "Solved");
addComponent(bugs);
Style.NO_PADDING.apply(this);
setCaption(CAPTION);
@ -66,7 +64,7 @@ public class BugTab extends VerticalLayout implements DataManager.Listener<AppSc
Optional<Bug> selection = e.getFirstSelectedItem();
ReportList reportList = null;
if (selection.isPresent()) {
reportList = new ReportList(app, navigationManager, dataManager, () -> dataManager.getReportsForBug(selection.get()),
reportList = new ReportList(app, navigationManager, dataManager, dataManager.getLazyReportsForBug(selection.get()),
reportInfo -> dataManager.matches(selection.get(), reportInfo));
replaceComponent(this.reportList, reportList);
} else if (this.reportList != null) {
@ -85,17 +83,8 @@ public class BugTab extends VerticalLayout implements DataManager.Listener<AppSc
private void setItems() {
getUI().access(() -> {
Set<Bug> selection = bugs.getSelectedItems();
bugs.setItems(getBugs());
bugs.setDataProvider(dataManager.getLazyBugs(app, !hideSolved.getValue()));
selection.forEach(bugs::select);
});
}
@NotNull
private List<Bug> getBugs() {
List<Bug> bugs = dataManager.getBugs(app);
if (hideSolved.getValue()) {
return bugs.stream().filter(bug -> !bug.isSolved()).collect(Collectors.toList());
}
return bugs;
}
}

View file

@ -11,6 +11,6 @@ import org.jetbrains.annotations.NotNull;
*/
public class ReportTab extends ReportList {
public ReportTab(@NotNull String app, @NotNull NavigationManager navigationManager, @NotNull DataManager dataManager) {
super(app, navigationManager, dataManager, () -> dataManager.getReportsForApp(app), reportInfo -> reportInfo.getApp().equals(app));
super(app, navigationManager, dataManager, dataManager.getLazyReportsForApp(app), reportInfo -> reportInfo.getApp().equals(app));
}
}

View file

@ -13,8 +13,6 @@ import java.util.Date;
* @since 26.05.2017
*/
public class TimeSpanRenderer extends TextRenderer {
public TimeSpanRenderer() {
}
@Override
public JsonValue encode(@Nullable Object value) {

View file

@ -6,7 +6,7 @@
<inherits name="org.vaadin.hene.popupbutton.widgetset.PopupbuttonWidgetset" />
<inherits name="org.vaadin.risto.stepper.StepperWidgetset" />
<set-configuration-property name="devModeRedirectEnabled" value="true" />
<set-property name="user.agent" value="ie8,ie9,gecko1_8,safari,ie10" />
<set-property name="user.agent" value="gecko1_8,safari,ie10" />
<source path="client" />
<source path="shared" />
<collapse-all-properties />