Skip to content

Commit

Permalink
feat: Reindex targeted identities when used dropdown option value tra…
Browse files Browse the repository at this point in the history
…nslation updated - EXO-8007 - Meeds-io/MIPs#171

Reindex targeted identities when used option value translation updated
  • Loading branch information
hakermi committed Jan 17, 2025
1 parent 7aad06b commit f4a3e78
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,25 @@
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Locale;
import java.util.Map;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProfilePropertyOption {

private Long id;
private Long id;

private String value;

private Long propertySettingId;

private String value;
private Map<String, String> translations;

private Long propertySettingId;
public ProfilePropertyOption(Long id, String value, Long propertySettingId) {
this.id = id;
this.value = value;
this.propertySettingId = propertySettingId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* This file is part of the Meeds project (https://meeds.io/).
*
* Copyright (C) 2025 Meeds Association [email protected]
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package org.exoplatform.social.core.listeners;

import io.meeds.common.ContainerTransactional;
import org.exoplatform.commons.search.index.IndexingService;
import org.exoplatform.commons.utils.ListAccess;
import org.exoplatform.services.listener.Asynchronous;
import org.exoplatform.services.listener.Event;
import org.exoplatform.services.listener.Listener;
import org.exoplatform.social.core.identity.model.Identity;
import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider;
import org.exoplatform.social.core.jpa.search.ProfileIndexingServiceConnector;
import org.exoplatform.social.core.manager.IdentityManager;
import org.exoplatform.social.core.profile.ProfileFilter;
import org.exoplatform.social.core.profileproperty.ProfilePropertyService;
import org.exoplatform.social.core.profileproperty.model.ProfilePropertyOption;
import org.exoplatform.social.core.profileproperty.model.ProfilePropertySetting;

import java.util.List;
import java.util.Locale;
import java.util.Map;

@Asynchronous
public class ProfilePropertySettingOptionTranslationListener extends Listener<ProfilePropertyOption, Map<Locale, String>> {

private final IdentityManager identityManager;

private final ProfilePropertyService profilePropertyService;

private final IndexingService indexingService;

public ProfilePropertySettingOptionTranslationListener(IdentityManager identityManager,
ProfilePropertyService profilePropertyService,
IndexingService indexingService) {
this.identityManager = identityManager;
this.profilePropertyService = profilePropertyService;
this.indexingService = indexingService;
}

@Override
@ContainerTransactional
public void onEvent(Event<ProfilePropertyOption, Map<Locale, String>> event) throws Exception {
ProfilePropertyOption profilePropertyOption = event.getSource();
Map<Locale, String> oldTranslations = event.getData();
ProfilePropertySetting propertySetting =
profilePropertyService.getProfileSettingById(profilePropertyOption.getPropertySettingId());
String translations = String.join("-", oldTranslations.values());
String indexedValue = String.join("-", profilePropertyOption.getValue(), translations);

ProfileFilter profileFilter = new ProfileFilter();
profileFilter.setProfileSettings(Map.of(propertySetting.getPropertyName(), indexedValue));
ListAccess<Identity> identities = identityManager.getIdentitiesByProfileFilter(OrganizationIdentityProvider.NAME,
profileFilter,
true);
List<Identity> identityList = List.of(identities.load(0, identities.getSize()));
for (Identity identity : identityList) {
indexingService.reindex(ProfileIndexingServiceConnector.TYPE, String.valueOf(identity.getId()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@

package org.exoplatform.social.core.profileproperty;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

import io.meeds.social.translation.model.TranslationField;
import io.meeds.social.translation.service.TranslationService;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.exoplatform.commons.exception.ObjectNotFoundException;
import org.exoplatform.social.core.profileproperty.model.ProfilePropertyOption;
import org.picocontainer.Startable;

import com.fasterxml.jackson.core.type.TypeReference;
Expand Down Expand Up @@ -57,6 +61,8 @@ public class ProfilePropertyServiceImpl implements ProfilePropertyService, Start
private final IndexingService indexingService;

private final ListenerService listenerService;

private final TranslationService translationService;

private static final String SYNCHRONIZED_DISABLED_PROPERTIES = "synchronizationDisabledProperties";

Expand All @@ -77,16 +83,21 @@ public class ProfilePropertyServiceImpl implements ProfilePropertyService, Start

private static final String HIDDEN_PROFILE_PROPERTY_SETTINGS_KEY = "HiddenProfilePropertySettings";

private static final String PROFILE_PROPERTY_FIELD_NAME = "optionValue";

private static final String PROFILE_PROPERTY_OBJECT_TYPE = "propertySettingOption";

public ProfilePropertyServiceImpl(InitParams params,
ProfileSettingStorage profileSettingStorage,
SettingService settingService,
IndexingService indexingService,
ListenerService listenerService) {
ListenerService listenerService, TranslationService translationService) {
this.profileSettingStorage = profileSettingStorage;
this.settingService = settingService;
this.indexingService = indexingService;
this.listenerService = listenerService;
if (params != null) {
this.translationService = translationService;
if (params != null) {
try {
synchronizedGroupDisabledProperties = Arrays.asList(params.getValueParam(SYNCHRONIZED_DISABLED_PROPERTIES)
.getValue()
Expand Down Expand Up @@ -156,21 +167,22 @@ public ProfilePropertySetting createPropertySetting(ProfilePropertySetting profi
}

profilePropertySetting.setUpdated(System.currentTimeMillis());
profilePropertySetting = profileSettingStorage.saveProfilePropertySetting(profilePropertySetting, true);

if (profilePropertySetting.getOrder() == null) {
profilePropertySetting.setOrder(profilePropertySetting.getId());
profilePropertySetting = profileSettingStorage.saveProfilePropertySetting(profilePropertySetting, false);
storedProfilePropertySetting = profileSettingStorage.saveProfilePropertySetting(profilePropertySetting,
true);
savePropertyOptionsTranslations(storedProfilePropertySetting, profilePropertySetting.getPropertyOptions(), false);
if (storedProfilePropertySetting.getOrder() == null) {
storedProfilePropertySetting.setOrder(storedProfilePropertySetting.getId());
storedProfilePropertySetting = profileSettingStorage.saveProfilePropertySetting(storedProfilePropertySetting, false);
}

try {
listenerService.broadcast("profile-property-setting-created", this, profilePropertySetting);
listenerService.broadcast("profile-property-setting-created", this, storedProfilePropertySetting);
} catch (Exception e) {
LOG.error("An error occurred while broadcasting the creation event for the property setting '{}'.",
profilePropertySetting.getPropertyName(),
storedProfilePropertySetting.getPropertyName(),
e);
}
return profilePropertySetting;
return storedProfilePropertySetting;
}

@Override
Expand All @@ -195,14 +207,15 @@ && getUnhiddenableProfileProperties().contains(profilePropertySetting.getPropert
profilePropertySetting.setPropertyType(createdProfilePropertySetting.getPropertyType());
}
profilePropertySetting.setUpdated(System.currentTimeMillis());
profileSettingStorage.saveProfilePropertySetting(profilePropertySetting, false);
ProfilePropertySetting updatedPropertySetting = profileSettingStorage.saveProfilePropertySetting(profilePropertySetting, false);
savePropertyOptionsTranslations(updatedPropertySetting, profilePropertySetting.getPropertyOptions(), true);
try {
listenerService.broadcast("profile-property-setting-updated", this, profilePropertySetting);
listenerService.broadcast("profile-property-setting-updated", this, updatedPropertySetting);
} catch (Exception e) {
LOG.error("An error occurred when broadcasting the update event of the property setting {}", profilePropertySetting.getPropertyName(), e);
LOG.error("An error occurred when broadcasting the update event of the property setting {}", updatedPropertySetting.getPropertyName(), e);
}
}

@Override
public void deleteProfilePropertySetting(Long id) {
if (id <= 0) {
Expand Down Expand Up @@ -337,4 +350,42 @@ private void validatePropertySetting(ProfilePropertySetting profilePropertySetti
throw new IllegalArgumentException("Only text properties can be dropdown lists.");
}
}

private void savePropertyOptionsTranslations(ProfilePropertySetting savedProfilePropertySetting,
List<ProfilePropertyOption> newOptions,
boolean update) {
if (!savedProfilePropertySetting.isDropdownList()) {
return;
}
List<ProfilePropertyOption> savedOptions =
profileSettingStorage.getProfilePropertyOptions(savedProfilePropertySetting.getId(),
0,
0);
for (int i = 0; i < savedOptions.size(); i++) {
ProfilePropertyOption savedOption = savedOptions.get(i);
ProfilePropertyOption newOption = i < newOptions.size() ? newOptions.get(i) : null;
if (savedOption == null || newOption == null) {
continue;
}
try {
TranslationField translationField = translationService.getTranslationField(PROFILE_PROPERTY_OBJECT_TYPE,
savedOption.getId(),
PROFILE_PROPERTY_FIELD_NAME);
Map<Locale, String> translations = newOption.getTranslations()
.entrySet()
.stream()
.collect(Collectors.toMap(entry -> Locale.forLanguageTag(entry.getKey()),
Map.Entry::getValue));
translationService.saveTranslationLabels(PROFILE_PROPERTY_OBJECT_TYPE,
savedOption.getId(),
PROFILE_PROPERTY_FIELD_NAME,
translations);
if (update && translationField != null && !translationField.getLabels().equals(translations)) {
listenerService.broadcast("property_options_updated", newOption, translationField.getLabels());
}
} catch (Exception e) {
LOG.error("Error while saving translation labels for profile property option {}", savedOption.getId(), e);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.exoplatform.social.core.jpa.test;

import org.exoplatform.social.core.listeners.ProfilePropertySettingOptionTranslationListenerTest;
import org.exoplatform.social.core.plugin.ProfilePropertySettingOptionTranslationTest;
import org.junit.AfterClass;
import org.junit.BeforeClass;
Expand Down Expand Up @@ -131,6 +132,7 @@
OrganizationalChartHeaderTranslationTest.class,
ManagerPropertySettingUpdatedListenerTest.class,
ProfilePropertySettingOptionTranslationTest.class,
ProfilePropertySettingOptionTranslationListenerTest.class,
})
@ConfigTestCase(AbstractCoreTest.class)
public class InitContainerTestSuite extends BaseExoContainerTestSuite {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* This file is part of the Meeds project (https://meeds.io/).
*
* Copyright (C) 2025 Meeds Association [email protected]
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.exoplatform.social.core.listeners;

import org.exoplatform.commons.search.index.IndexingService;
import org.exoplatform.commons.utils.ListAccess;
import org.exoplatform.services.listener.Event;
import org.exoplatform.social.core.identity.model.Identity;
import org.exoplatform.social.core.jpa.search.ProfileIndexingServiceConnector;
import org.exoplatform.social.core.manager.IdentityManager;
import org.exoplatform.social.core.profileproperty.ProfilePropertyService;
import org.exoplatform.social.core.profileproperty.model.ProfilePropertyOption;
import org.exoplatform.social.core.profileproperty.model.ProfilePropertySetting;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.util.*;

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class ProfilePropertySettingOptionTranslationListenerTest {

@Mock
private ProfilePropertyService profilePropertyService;

@Mock
private IdentityManager identityManager;

@Mock
private IndexingService indexingService;

private ProfilePropertySettingOptionTranslationListener profilePropertySettingOptionListener;

@Before
public void setUp() throws Exception {
profilePropertySettingOptionListener = new ProfilePropertySettingOptionTranslationListener(identityManager,
profilePropertyService,
indexingService);
}

@Test
public void testOnEvent() throws Exception {
Identity identity1 = mock(Identity.class);
Identity identity2 = mock(Identity.class);

when(identity1.getId()).thenReturn("identity1");
when(identity2.getId()).thenReturn("identity2");

Map<Locale, String> labels = new HashMap<>();
labels.put(Locale.US, "option en");
labels.put(Locale.FRANCE, "option fr");

ProfilePropertySetting profilePropertySetting = new ProfilePropertySetting();
profilePropertySetting.setId(1L);
profilePropertySetting.setPropertyName("test1");

ProfilePropertyOption profilePropertyOption1 = new ProfilePropertyOption();
profilePropertyOption1.setId(1L);
profilePropertyOption1.setValue("value");
profilePropertyOption1.setPropertySettingId(1L);

Event<ProfilePropertyOption, Map<Locale, String>> event = new Event<>("property_options_updated",
profilePropertyOption1,
labels);

ListAccess<Identity> identityListAccess = new ListAccess<>() {
public Identity[] load(int index, int length) {
List<Identity> identities = new ArrayList<>();
identities.add(identity1);
identities.add(identity2);
Identity[] result = new Identity[identities.size()];
return identities.toArray(result);
}

public int getSize() {
return 2;
}
};
when(profilePropertyService.getProfileSettingById(eq(1L))).thenReturn(profilePropertySetting);
when(identityManager.getIdentitiesByProfileFilter(anyString(), any(), anyBoolean())).thenReturn(identityListAccess);
profilePropertySettingOptionListener.onEvent(event);
verify(indexingService, times(1)).reindex(ProfileIndexingServiceConnector.TYPE, "identity1");
verify(indexingService, times(1)).reindex(ProfileIndexingServiceConnector.TYPE, "identity1");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2127,7 +2127,8 @@ public static List<ProfilePropertyOption> toProfilePropertyOptions(List<ProfileP
return profilePropertyOptionEntities.stream()
.map(option -> new ProfilePropertyOption(option.getId(),
option.getValue(),
option.getPropertySettingId()))
option.getPropertySettingId(),
option.getTranslations()))
.toList();
}

Expand Down
Loading

0 comments on commit f4a3e78

Please sign in to comment.