При открытии пользователя на редактирование не загружется группа этого пользователя

Используемая версия Кубы 7.2.17

Мы создали расширение стандартного редактора пользователей таким образом, чтобы было три колонки вместо двух, выделив поля firstName, middleName, lastName и position в новую третью колонку, так как при используемых нашими клиентами разрешениях экрана три колонки удобнее.

ext-user-edit.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        xmlns:ext="http://schemas.haulmont.com/cuba/window-ext.xsd"
        class="firm.project.web.screens.user.edit.ExtUserEditor"
        extends="com/haulmont/cuba/gui/app/security/user/edit/user-edit.xml"
        messagesPack="firm.project.web.screens.user.edit">
    <dialogMode width="1280px" modal="true" forceDialog="true"/>
    <layout>
        <groupBox id="propertiesBox">
            <grid id="propertiesGrid">
                <columns>
                    <column id="fieldGroupLeftColumn"/>
                    <column id="fieldGroupRightColumn"/>
                    <column id="fieldGroupNewColumn" flex="5"/>
                </columns>
                <rows>
                    <row id="propertiesRow">
                        <fieldGroup id="fieldGroupLeft">
                            <column>
                                <field id="firstName" visible="false"/>
                                <field id="middleName" visible="false"/>
                                <field id="lastName" visible="false"/>
                                <field id="name" required="true"/>
                            </column>
                        </fieldGroup>

                        <fieldGroup id="fieldGroupRight">
                            <column>
                                <field id="position" visible="false"/>
                            </column>
                        </fieldGroup>

                        <fieldGroup id="fieldGroupNew" datasource="userDs" width="AUTO">
                            <column>
                                <field id="firstName" property="firstName"/>
                                <field id="middleName" property="middleName"/>
                                <field id="lastName" property="lastName"/>
                                <field id="position" property="position"/>
                            </column>
                        </fieldGroup>
                    </row>
                </rows>
            </grid>
        </groupBox>
    </layout>
</window>

Как видно, поле группы не затронуто вовсе.

Изменений в контроллере по сути нет вообще

ExtUserEditor.kt
package firm.project.web.screens.user.edit

import com.haulmont.cuba.gui.app.security.user.edit.UserEditor

class ExtUserEditor : UserEditor()

С несколькими предыдущими версиями CUBA это работало отлично, но после перехода на 7.2.17 начали замечать иногда появляющуюся ошибку, при которой открытый на редактирование пользователь имел поле группы пустым, хотя, разумеется, это поле обязательно и в таблице группа у редактируемого пользователя отображалась корректно.

Хотелось бы понять, чем это вызвано и устранить проблему. Если же это вина наших модификаций редактора, хотелось бы получить информацию, что именно мы сделали не так и как разбить редактор на три колонки правильно.

Добрый день, Stanislav. К сожалению воспроизвести вашу проблему не удалось, как при создании расширения редактора на версии 7.2.17 так и при миграции с предыдущей версии с уже добавленным функционалом.
Если вопрос все еще актуален для вас, то для локализации ошибки необходимо следующее:

  1. Подробные шаги воспроизведения (начиная со входа в приложение) и поля которые заполнены в сущности пользователя на момент воспроизведения ошибки
  2. Окружение в котором работает приложение
  3. Опционально, но очень могло бы помочь в поиске причин ошибки (демо проект на котором эта ошибка хоть раз воспроизводилась)
  4. Тоже опционально, если вспомните (версия платформы, в которой был добавлен функционал)

Шаги по воспроизведению:

Зайти в приложение, выбрать в меню Администирование - Пользователи. Выбрать пользователя. Нажать кнопку “Изменить” и увидеть пустое поле группы.

Заполненные поля: Логин, Полное имя, Email, Группа (но не отображается), Язык, Часовой пояс установлен в Авто, Активен - да, Имя, Фамилия, Должность

Единственное обнаруженное отличие от других пользователей - Часовой пояс установлен в Авто, у остальных часовой пояс не заполнен

Окружение:
Ubuntu 20.04 LTS (GNU/Linux 5.4.0-26-generic x86_64)
изображение
Компоненты приложения
com.haulmont.cuba:7.2.17, com.haulmont.charts:7.2.17, de.diedavids.cuba.entitysoftreference:0.7.0, de.balvi.cuba.declarativecontrollers:0.10.0, de.diedavids.cuba.taggable:0.6.0, com.haulmont.addon.restapi:7.2.2, com.haulmont.addon.cubaaws:1.0.1, com.haulmont.addon.imap:1.5.1, com.haulmont.fts:7.2.14, com.haulmont.addon.helium:0.4.0, com.haulmont.addon.admintools:1.5.1, com.haulmont.addon.globalevents:0.6.1, com.haulmont.reports:7.2.17, it.nexbit.cuba.security.forgotpassword:4.0.0

Плюс наши библиотеки tech.opus.tm:1.4.2, ru.itsyn.common:3.3.7, ru.itsyn.esb:3.4.9, ru.itsyn.cuba.menu_editor:0.5, которые не затрагивают пользователей.

Свежий комментарий тестера

“Только что на тестовом сервере на моих двух пользователях не было группы. Вчера они были, сейчас вообще ничего не меняла, открыла снова их и все есть у них )))) Не знаю в общем куда копать”

Прошу прощения за долгий ответ. Проблема всё ещё актуальна? Вы не смогли обнаружить, в чем причина такого поведения?

Причину поведения найти так и не смогли. В качестве решения проблемы мы принудительно приделали заплатку - в расширении редактора переписали метод setUserGroupValue для принудительной перезагрузки группы если её загрузить не удалось.

@Inject
private lateinit var dataManager: DataManager

override fun setUserGroupValue() {
    if (editedEntity.group?.name != null) {
        groupField.setValue(editedEntity.group)
    } else if (editedEntity.groupNames != null) {
        groupField.value = groupsService.findPredefinedGroupByName(editedEntity.groupNames)
    } else if (!PersistenceHelper.isNew(editedEntity)) {
        val loadContext = LoadContext.create(Group::class.java)
        loadContext.query = LoadContext.createQuery(
                "select g from sec\$Group g where g.id in (select u.group.id from sec\$User u where u.id = :userId)")
                .setParameter("userId", editedEntity.id)
        loadContext.setView("_local")
        dataManager.loadList(loadContext).firstOrNull()?.let {
            groupField.value = it
            editedEntity.group = it
            (userDs as DatasourceImpl).isModified = false
        }
    }
}

Но по сути проблема остается, мы эту дыру просто заткнули.

Добрый день!
Проверьте, пожалуйста, дебагом в изначальном методе setUserGroupValue() какой именно метод из проверок не возвращает имя группы для пользователя – getGroup() или getGroupName(). Исходя из этого, мы сможем предложить дальнейшие шаги решения проблемы.
Так же уточните, пожалуйста, не был ли изменен браузер для сущности пользователя?

Если вы можете создать демо проект, в котором будет возпроизводиться ошибка, то в таком случае мы точно сможем определить причину ошибки.

У пользователей, на которых мы наблюдали данную ошибку группа была, т.е. getGroup() должен был вернуть группу с именем, но почему-то возвращал без имени.

В браузер была добавлена колонка с ролями.

ext-user-browse.xml <?xml version="1.0" encoding="UTF-8" standalone="no"?>



ExtUserBrowser.java

package tech.opus.wp.web.screens.user.browse;

import com.haulmont.cuba.gui.app.security.user.browse.UserBrowser;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.Table;
import com.haulmont.cuba.gui.screen.MessageBundle;
import com.haulmont.cuba.security.entity.Role;
import com.haulmont.cuba.security.entity.User;
import com.haulmont.cuba.security.entity.UserRole;
import org.jetbrains.annotations.NotNull;

import javax.inject.Inject;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class ExtUserBrowser extends UserBrowser {

@Inject
private MessageBundle messageBundle;

@Override
public void init(Map<String, Object> params) {
    super.init(params);
    addRolesColumn();
    copyAction.setVisible(false);
}

public void addRolesColumn() {
    usersTable.addGeneratedColumn("roles", new Table.PrintableColumnGenerator<User, String>() {

        @Override
        public @NotNull String getValue(User item) {
            return getRoles(item);
        }

        @Override
        public Component generateCell(@NotNull User entity) {
            return new Table.PlainTextCell(getRoles(entity));
        }

        private String getRoles(User user) {
            List<UserRole> userRoles = user.getUserRoles();
            if (userRoles != null) {
                return userRoles.stream()
                        .sorted((o1, o2) -> {
                            Role o1Role = o1.getRole();
                            Role o2Role = o2.getRole();
                            if (o1Role == null) return 1;
                            if (o2Role == null) return -1;
                            return o2Role.getCreateTs().compareTo(o1Role.getCreateTs());
                        })
                        .map(userRole -> {
                            Role role = userRole.getRole();
                            // system roles such as 'system-full-access' do not have record in db,
                            // for other roles parameter userRole#roleName is null,
                            // name is taken from userRole#role#name
                            if (role != null) {
                                return role.getName();
                            }
                            return userRole.getRoleName();
                        })
                        .collect(Collectors.joining(", "));
            }
            return null;
        }
    });

    Table.Column<User> rolesColumn = usersTable.getColumn("roles");
    rolesColumn.setCaption(messageBundle.getMessage("rolesColumn"));
}

}

Группы доступа могут быть заданы в коде приложения (design-time) или во время выполнения (run-time), нужно определить, какой тип групп не находится. Для получения данных об этих группах используются разные методы, поэтому я и прошу уточнить, какой именно метод не отрабатывает в коде приложения.

Метод getGroup. У всех без исключения пользователей, у которых мы видели ошибку, группа хранится в базе.

Этот метод возвращает null или все, кроме имени группы?

Насколько я видел по дебагу, он возвращает группу без её имени.

Такая же проблема
Используемая версия Кубы 7.2.18

IllegalStateException: Cannot get unfetched attribute [roleName] from detached object com.haulmont.cuba.security.entity.UserRole-6736effb-9dfc-4430-973a-69868606b09c [detached].
java.lang.IllegalStateException: Cannot get unfetched attribute [roleName] from detached object com.haulmont.cuba.security.entity.UserRole-6736effb-9dfc-4430-973a-69868606b09c [detached].
	at org.eclipse.persistence.internal.queries.EntityFetchGroup.onUnfetchedAttribute(EntityFetchGroup.java:100)
	at com.haulmont.cuba.core.sys.persistence.CubaEntityFetchGroup.onUnfetchedAttribute(CubaEntityFetchGroup.java:74)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.processUnfetchedAttribute(EntityManagerImpl.java:2998)
	at com.haulmont.chile.core.model.impl.AbstractInstance._persistence_checkFetched(AbstractInstance.java)
	at com.haulmont.cuba.security.entity.UserRole._persistence_get_roleName(UserRole.java)
	at com.haulmont.cuba.security.entity.UserRole.getRoleName(UserRole.java:70)
	at com.haulmont.cuba.gui.app.security.user.edit.UserEditor.filterRolesDs(UserEditor.java:283)
	at com.haulmont.cuba.gui.app.security.user.edit.UserEditor.postInit(UserEditor.java:257)
	at com.haulmont.cuba.gui.components.AbstractEditor.setItem(AbstractEditor.java:160)
	at com.haulmont.cuba.gui.components.AbstractEditor.setEntityToEdit(AbstractEditor.java:451)
	at com.haulmont.cuba.web.sys.WebScreens.openEditor(WebScreens.java:1253)
	at com.haulmont.cuba.gui.components.HasWindowManager.openEditor(HasWindowManager.java:138)
	at com.haulmont.cuba.gui.components.actions.EditAction.internalOpenEditor(EditAction.java:288)
	at com.haulmont.cuba.gui.components.actions.EditAction.actionPerform(EditAction.java:232)
	at com.haulmont.cuba.web.gui.components.WebAbstractTable.handleClickAction(WebAbstractTable.java:1295)
	at com.haulmont.cuba.web.gui.components.WebAbstractTable.lambda$initComponent$db766529$1(WebAbstractTable.java:1048)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:709)
	at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:399)
	at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:363)
	at com.vaadin.server.AbstractClientConnector.fireEvent(AbstractClientConnector.java:1217)
	at com.vaadin.v7.ui.Table.handleClickEvent(Table.java:3197)
	at com.vaadin.v7.ui.Table.changeVariables(Table.java:2989)
	at com.haulmont.cuba.web.widgets.CubaTable.changeVariables(CubaTable.java:367)
	at com.haulmont.cuba.web.widgets.CubaGroupTable.changeVariables(CubaGroupTable.java:297)
	at com.vaadin.server.communication.ServerRpcHandler.changeVariables(ServerRpcHandler.java:616)
	at com.vaadin.server.communication.ServerRpcHandler.handleInvocation(ServerRpcHandler.java:468)
	at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:411)
	at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:275)
	at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:83)
	at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40)
	at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1636)
	at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:465)
	at com.haulmont.cuba.web.sys.CubaApplicationServlet.serviceAppRequest(CubaApplicationServlet.java:329)
	at com.haulmont.cuba.web.sys.CubaApplicationServlet.service(CubaApplicationServlet.java:215)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108)
	at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74)
	at com.haulmont.cuba.web.sys.CubaHttpFilter.doFilter(CubaHttpFilter.java:93)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:678)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1579)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.base/java.lang.Thread.run(Thread.java:829)

Проблема unfetched attribute возникает в том случае, когда вы пытаетесь получить атрибут, не включенный в представление сущности. Пожалуйста, создайте отдельный топик и распишите более детально, где именно и в каком случае возникает эта проблема.