Skip to content

Commit

Permalink
chore: Update to Vaadin 8.27.6
Browse files Browse the repository at this point in the history
feat: Allow immediate revoke of Admin.ROLE, and immediate current user update
feat: Update Charts in StatsView when Category is updated / removed
fix: Typo in language key
fix: Use Executor in EventBus to avoid blockign foreign request thread
  • Loading branch information
TatuJLund committed Feb 2, 2025
1 parent bd1c829 commit ca64247
Show file tree
Hide file tree
Showing 17 changed files with 273 additions and 42 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
</prerequisites>

<properties>
<vaadin.version>8.27.4</vaadin.version>
<vaadin.version>8.27.6</vaadin.version>
<vaadin.plugin.version>${vaadin.version}</vaadin.plugin.version>
<logback.version>1.3.15</logback.version>
<slf4j.version>2.0.9</slf4j.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public AboutView() {
if (accessControl.isUserInRole(Role.ADMIN)) {
createEditButton();
editButton.addClickListener(e -> {
accessControl.assertAdmin();
adminsNoteField.setVisible(true);
adminsNote.setVisible(false);
editButton.setVisible(false);
Expand All @@ -79,7 +80,7 @@ public AboutView() {

adminsNoteField.addValueChangeListener(this::handleValueChange);
adminsNoteField.setValueChangeMode(ValueChangeMode.BLUR);
adminsNoteField.addBlurListener(e -> closeEditor());
adminsNoteField.addBlurListener(blurEvent -> closeEditor());
setSizeFull();
setMargin(false);
setStyleName(VaadinCreateTheme.ABOUT_VIEW);
Expand All @@ -98,9 +99,9 @@ private void closeEditor() {
}
}

private void handleValueChange(ValueChangeEvent<String> e) {
if (e.isUserOriginated()) {
var unsanitized = e.getValue();
private void handleValueChange(ValueChangeEvent<String> valueChange) {
if (valueChange.isUserOriginated()) {
var unsanitized = valueChange.getValue();
// Sanitize user input with Jsoup to avoid JavaScript injection
// vulnerabilities
var text = Utils.sanitize(unsanitized);
Expand Down Expand Up @@ -140,7 +141,7 @@ private TextArea createTextArea() {
textArea.setIcon(VaadinIcons.FILE_TEXT_O);
textArea.setCaption("HTML");
textArea.setPlaceholder("max 250 chars");
textArea.addFocusListener(e -> saveRegistration = textArea
textArea.addFocusListener(focusEvent -> saveRegistration = textArea
.addShortcutListener(new ShortcutListener("Save", KeyCode.S,
new int[] { ModifierKey.CTRL }) {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.slf4j.MDC;
import org.vaadin.tatu.vaadincreate.AboutView.MessageEvent;
import org.vaadin.tatu.vaadincreate.admin.AdminView;
import org.vaadin.tatu.vaadincreate.admin.UserManagementPresenter.UserUpdatedEvent;
import org.vaadin.tatu.vaadincreate.auth.AccessControl;
import org.vaadin.tatu.vaadincreate.auth.BasicAccessControl;
import org.vaadin.tatu.vaadincreate.auth.CurrentUser;
Expand Down Expand Up @@ -80,6 +81,9 @@ protected void init(VaadinRequest request) {
MDC.clear();
showLoginView();
} else {
var user = CurrentUser.get().get();
user = getUserService().getUserById(user.getId());
CurrentUser.set(user);
target = getInitialTarget();
showAppLayout();
}
Expand Down Expand Up @@ -129,7 +133,7 @@ protected void showAppLayout() {
VaadinIcons.USERS, AdminView.VIEW_NAME);

Product draft = getProductService().findDraft(CurrentUser.get().get());
if (draft != null) {
if (draft != null && getAccessControl().isUserInRole(User.Role.ADMIN)) {
handleDraft(draft);
}
if (target.isEmpty()) {
Expand Down Expand Up @@ -233,6 +237,16 @@ public void eventFired(Object event) {
note.show(getPage());
});
}
if (event instanceof UserUpdatedEvent userUpdated) {
var user = getSession().getSession().getAttribute(
CurrentUser.CURRENT_USER_SESSION_ATTRIBUTE_KEY);
if (user.equals(userUpdated.user())) {
logger.debug("User was updated, updating CurrentUser");
access(() -> getSession().getSession().setAttribute(
CurrentUser.CURRENT_USER_SESSION_ATTRIBUTE_KEY,
userUpdated.user()));
}
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.vaadin.tatu.vaadincreate.auth.AccessControl;
import org.vaadin.tatu.vaadincreate.backend.ProductDataService;
import org.vaadin.tatu.vaadincreate.backend.data.Category;
import org.vaadin.tatu.vaadincreate.eventbus.EventBus;

@SuppressWarnings("serial")
public class CategoryManagementPresenter implements Serializable {
Expand Down Expand Up @@ -40,6 +41,8 @@ public void removeCategory(Category category) {
accessControl.assertAdmin();
try {
getService().deleteCategory(category.getId());
getEventBus().post(new CategoriesUpdatedEvent(category,
CategoriesUpdatedEvent.CategoryChange.DELETE));
view.showDeleted(category.getName());
} catch (IllegalArgumentException e) {
view.showDeleteError();
Expand All @@ -60,6 +63,9 @@ public Category updateCategory(Category category) {
try {
newCat = getService().updateCategory(category);
logger.info("Category '{}' updated.", category.getName());
getEventBus().post(new CategoriesUpdatedEvent(newCat,
CategoriesUpdatedEvent.CategoryChange.SAVE));

} catch (OptimisticLockException | IllegalStateException
| IllegalArgumentException e) {
requestUpdateCategories();
Expand All @@ -68,10 +74,21 @@ public Category updateCategory(Category category) {
return newCat;
}

private EventBus getEventBus() {
return EventBus.get();
}

private ProductDataService getService() {
return VaadinCreateUI.get().getProductService();
}

public record CategoriesUpdatedEvent(Category category,
CategoryChange change) {
public enum CategoryChange {
SAVE, DELETE
}
}

private static Logger logger = LoggerFactory
.getLogger(CategoryManagementPresenter.class);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import org.vaadin.tatu.vaadincreate.AttributeExtension;
import org.vaadin.tatu.vaadincreate.VaadinCreateTheme;
import org.vaadin.tatu.vaadincreate.auth.CurrentUser;
import org.vaadin.tatu.vaadincreate.backend.data.User;
import org.vaadin.tatu.vaadincreate.backend.data.User.Role;
import org.vaadin.tatu.vaadincreate.i18n.HasI18N;
Expand Down Expand Up @@ -33,6 +34,7 @@ public class UserForm extends Composite implements HasI18N {
private PasswordField password2;
private TextField username;
private User user;
private ComboBox<Role> role;

public UserForm() {
form.addStyleName(VaadinCreateTheme.ADMINVIEW_USERFORM);
Expand All @@ -45,7 +47,7 @@ public UserForm() {
password.setId("password-field");
password2 = new PasswordField(getTranslation(I18n.User.PASSWD_REPEAT));
password2.setId("password-repeat");
var role = new ComboBox<Role>(getTranslation(I18n.User.ROLE));
role = new ComboBox<>(getTranslation(I18n.User.ROLE));
role.setItems(Role.values());
role.setEmptySelectionAllowed(false);
role.setTextInputAllowed(false);
Expand Down Expand Up @@ -85,6 +87,8 @@ public void populate(User user) {
password2.setValue(user.getPasswd());
setEnabled(true);
username.focus();
// Cannot change own role
role.setEnabled(!CurrentUser.get().get().equals(user));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.vaadin.tatu.vaadincreate.auth.AccessControl;
import org.vaadin.tatu.vaadincreate.backend.UserService;
import org.vaadin.tatu.vaadincreate.backend.data.User;
import org.vaadin.tatu.vaadincreate.eventbus.EventBus;

@SuppressWarnings("serial")
public class UserManagementPresenter implements Serializable {
Expand Down Expand Up @@ -39,7 +40,8 @@ public void removeUser(Integer id) {
public void updateUser(User user) {
accessControl.assertAdmin();
try {
getService().updateUser(user);
var updatedUser = getService().updateUser(user);
getEventBus().post(new UserUpdatedEvent(updatedUser));
view.showUserUpdated();
requestUpdateUsers();
logger.info("User {}/'{}' updated.", user.getId(), user.getName());
Expand All @@ -51,10 +53,17 @@ public void updateUser(User user) {
}
}

private EventBus getEventBus() {
return EventBus.get();
}

private UserService getService() {
return VaadinCreateUI.get().getUserService();
}

public record UserUpdatedEvent(User user) {
}

private static Logger logger = LoggerFactory
.getLogger(UserManagementPresenter.class);
}
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ public void selectProduct(Product product) {
* the product draft to be saved
*/
public void saveDraft(Product draft) {
accessControl.assertAdmin();
getService().saveDraft(CurrentUser.get().get(), draft);
this.draft = draft;
}
Expand All @@ -386,6 +387,7 @@ public void saveDraft(Product draft) {
* Removes the current draft.
*/
public void removeDraft() {
accessControl.assertAdmin();
draft = null;
getService().saveDraft(CurrentUser.get().get(), null);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.vaadin.tatu.vaadincreate.eventbus;

import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -21,6 +23,8 @@ public class EventBusImpl implements EventBus {
*/
private final WeakHashMap<EventBusListener, Object> eventListeners = new WeakHashMap<>();

private final ExecutorService executor = Executors.newFixedThreadPool(5);

public static synchronized EventBus getInstance() {
if (instance == null) {
instance = new EventBusImpl();
Expand All @@ -36,7 +40,8 @@ public void post(Object event) {
synchronized (eventListeners) {
logger.debug("EventBus event fired for {} recipients.",
eventListeners.size());
eventListeners.forEach((listener, o) -> listener.eventFired(event));
eventListeners.forEach((listener, o) -> executor
.execute(() -> listener.eventFired(event)));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vaadin.tatu.vaadincreate.VaadinCreateUI;
import org.vaadin.tatu.vaadincreate.admin.CategoryManagementPresenter.CategoriesUpdatedEvent;
import org.vaadin.tatu.vaadincreate.backend.ProductDataService;
import org.vaadin.tatu.vaadincreate.backend.data.Availability;
import org.vaadin.tatu.vaadincreate.backend.data.Category;
Expand All @@ -30,15 +31,19 @@ public class StatsPresenter implements EventBusListener, Serializable {
private StatsView view;
private transient CompletableFuture<Void> future;
private static final String PRODUCTS_NOT_NULL = "Products must not be null";
private transient ProductDataService service = VaadinCreateUI.get()
.getProductService();
private transient ExecutorService executor = VaadinCreateUI.get()
.getExecutor();

public StatsPresenter(StatsView view) {
this.view = view;
getEventBus().registerEventBusListener(this);
}

private CompletableFuture<Collection<Product>> loadProductsAsync() {
var service = getService();
return CompletableFuture.supplyAsync(service::getAllProducts,
var productService = getService();
return CompletableFuture.supplyAsync(productService::getAllProducts,
getExecutor());
}

Expand All @@ -48,15 +53,14 @@ private CompletableFuture<Collection<Product>> loadProductsAsync() {
*/
public void requestUpdateStats() {
logger.info("Fetching products for statistics");
var service = getService();
future = loadProductsAsync().thenAccept(products -> {
logger.info("Calculating statistics");

Map<Availability, Long> availabilityStats = calculateAvailabilityStats(
products);

Map<String, Long[]> categoryStats = calculateCategoryStats(service,
products);
Map<String, Long[]> categoryStats = calculateCategoryStats(
getService(), products);
// filter out empty categories
categoryStats.entrySet()
.removeIf(entry -> entry.getValue()[0] == 0);
Expand Down Expand Up @@ -136,11 +140,17 @@ private List<PriceBracket> getPriceBrackets(Collection<Product> products) {
}

private ProductDataService getService() {
return VaadinCreateUI.get().getProductService();
if (service == null) {
service = VaadinCreateUI.get().getProductService();
}
return service;
}

private ExecutorService getExecutor() {
return VaadinCreateUI.get().getExecutor();
if (executor == null) {
executor = VaadinCreateUI.get().getExecutor();
}
return executor;
}

private EventBus getEventBus() {
Expand All @@ -150,9 +160,11 @@ private EventBus getEventBus() {
@Override
public void eventFired(Object event) {
// Update statistics when new product is added
if (event instanceof BooksChanged) {
logger.info("Book saved or deleted, refreshing statistics.");
view.setLoading();
if (event instanceof BooksChanged
|| event instanceof CategoriesUpdatedEvent) {
logger.info(
"Book or Category saved or deleted, refreshing statistics.");
view.setLoadingAsync();
requestUpdateStats();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ private void updatePriceChart(Map<String, Long> priceStats) {
updatePriceChartAccessibilityAttributes(priceSeries);
}

@SuppressWarnings("java:S1192")
private void updatePriceChartAccessibilityAttributes(
DataSeries priceSeries) {
priceChart.setAttribute("role", "figure");
Expand Down Expand Up @@ -351,9 +352,11 @@ public void detach() {
presenter.cancelUpdateStats();
}

public void setLoading() {
dashboard.removeStyleName("loaded");
categoryChart.getConfiguration().removeyAxes();
public void setLoadingAsync() {
Utils.access(ui, () -> {
dashboard.removeStyleName("loaded");
categoryChart.getConfiguration().removeyAxes();
});
}

/**
Expand All @@ -372,8 +375,10 @@ public CustomChart(ChartType type) {
/**
* Sets an attribute with the specified key and value.
*
* @param key the attribute key
* @param value the attribute value
* @param key
* the attribute key
* @param value
* the attribute value
*/
public void setAttribute(String key, String value) {
attributes.setAttribute(key, value);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
product.name.required=Tuotteen nimi vaaditaan
product.name.min2max200=Tuotteen nimi pitää olla vähintään kaksi merkkiä ja enintään 100
stock.not.negative=Varaston määrä ei saa olla negatiivinen
stock.require=Varaston määrä vaaditaan
stock.required=Varaston määrä vaaditaan
price.not.negative=Hinta ei voi olla negativiinen
availability.required=Saatavuus vaaditaan
user.name.required=Käyttäjän nimi vaaditaan
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ protected void waitForGrid(HasComponents layout, BookGrid grid) {
* The dashboard
*/
protected void waitForCharts(CssLayout dashboard) {
assertFalse(dashboard.getStyleName().contains("loaded"));
waitWhile(dashboard, d -> !d.getStyleName().contains("loaded"), 15);
}

Expand Down
Loading

0 comments on commit ca64247

Please sign in to comment.