Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add an upgrade plugin for content pages properties migration - EXO-73181 - Meeds-io/MIPs#128 #240

Merged
merged 1 commit into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
* Copyright (C) 2024 eXo Platform SAS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.exoplatform.news.upgrade;

import io.meeds.notes.model.NoteMetadataObject;
import org.apache.commons.collections4.MapUtils;
import org.exoplatform.commons.upgrade.UpgradeProductPlugin;
import org.exoplatform.container.xml.InitParams;
import org.exoplatform.portal.config.UserACL;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.social.core.manager.IdentityManager;
import org.exoplatform.social.core.space.model.Space;
import org.exoplatform.social.core.space.spi.SpaceService;
import org.exoplatform.social.metadata.MetadataFilter;
import org.exoplatform.social.metadata.MetadataService;
import org.exoplatform.social.metadata.model.MetadataItem;
import org.exoplatform.social.metadata.model.MetadataKey;
import org.exoplatform.wiki.model.Page;
import org.exoplatform.wiki.model.PageVersion;
import org.exoplatform.wiki.service.NoteService;
import org.exoplatform.wiki.utils.Utils;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ContentArticlePropertiesUpgrade extends UpgradeProductPlugin {

private static final Log LOG =
ExoLogger.getLogger(ContentArticlePropertiesUpgrade.class);

private final NoteService noteService;

private final MetadataService metadataService;

private final IdentityManager identityManager;

private final SpaceService spaceService;

private final UserACL userACL;

private static final MetadataKey NOTES_METADATA_KEY =
new MetadataKey("notes", Utils.NOTES_METADATA_OBJECT_TYPE, 0);

public static final String NEWS_METADATA_NAME = "news";

public static final String NEWS_METADATA_DRAFT_OBJECT_TYPE = "newsDraftPage";

public static final String NOTE_METADATA_PAGE_OBJECT_TYPE = "notePage";

public static final String NOTE_METADATA_DRAFT_PAGE_OBJECT_TYPE = "noteDraftPage";

public static final String NEWS_METADATA_PAGE_VERSION_OBJECT_TYPE = "newsPageVersion";

public static final String NEWS_METADATA_LATEST_DRAFT_OBJECT_TYPE = "newsLatestDraftPage";

public static final String CONTENT_ILLUSTRATION_ID = "illustrationId";

public static final String SUMMARY = "summary";

public static final String FEATURED_IMAGE_ID = "featuredImageId";

private static final String FEATURED_IMAGE_UPDATED_DATE = "featuredImageUpdatedDate";

public ContentArticlePropertiesUpgrade(InitParams initParams,
NoteService noteService,
MetadataService metadataService,
IdentityManager identityManager,
SpaceService spaceService,
UserACL userACL) {
super(initParams);
this.noteService = noteService;
this.metadataService = metadataService;
this.identityManager = identityManager;
this.spaceService = spaceService;
this.userACL = userACL;
}

@Override
public void processUpgrade(String oldVersion, String newVersion) {
long startupTime = System.currentTimeMillis();
LOG.info("Start upgrade of content page properties");
int notMigratedContentPagesPropertiesCount;
int processedContentPagesPropertiesCount = 0;
int totalContentPagesPropertiesCount = 0;
try {
MetadataFilter metadataFilter = getMetadataFilter();
metadataFilter.setMetadataObjectTypes(List.of(NEWS_METADATA_PAGE_VERSION_OBJECT_TYPE,
NEWS_METADATA_DRAFT_OBJECT_TYPE,
NEWS_METADATA_LATEST_DRAFT_OBJECT_TYPE));
List<MetadataItem> metadataItems = metadataService.getMetadataItemsByFilter(metadataFilter, 0, 0);
totalContentPagesPropertiesCount = metadataItems.size();
for (MetadataItem metadataItem : metadataItems) {
if (metadataItem != null && !MapUtils.isEmpty(metadataItem.getProperties())) {
Map<String, String> contentProperties = metadataItem.getProperties();
Page page = null;
String objectType = NOTE_METADATA_PAGE_OBJECT_TYPE;
if (metadataItem.getObjectType().equals(NEWS_METADATA_PAGE_VERSION_OBJECT_TYPE)) {
PageVersion pageVersion = noteService.getPageVersionById(Long.valueOf(metadataItem.getObjectId()));
if (pageVersion != null && pageVersion.getParent() != null) {
page = pageVersion.getParent();
}
} else {
page = noteService.getDraftNoteById(metadataItem.getObjectId(), userACL.getSuperUser());
objectType = NOTE_METADATA_DRAFT_PAGE_OBJECT_TYPE;
}
if (page != null && page.getAuthor() != null) {
NoteMetadataObject noteMetadataObject = buildNoteMetadataObject(page, null, objectType);
MetadataItem noteMetadataItem = getNoteMetadataItem(page, null, objectType);
Map<String, String> noteProperties = new HashMap<>();
long creatorId = Long.parseLong(identityManager.getOrCreateUserIdentity(page.getAuthor()).getId());
hakermi marked this conversation as resolved.
Show resolved Hide resolved
if (noteMetadataItem != null && noteMetadataItem.getProperties() != null) {
noteProperties = noteMetadataItem.getProperties();
}
if (noteProperties.getOrDefault(CONTENT_ILLUSTRATION_ID, null) != null) {
noteProperties.put(FEATURED_IMAGE_ID, contentProperties.get(CONTENT_ILLUSTRATION_ID));
noteProperties.put(FEATURED_IMAGE_UPDATED_DATE, String.valueOf(new Date().getTime()));
hakermi marked this conversation as resolved.
Show resolved Hide resolved
}
noteProperties.put(SUMMARY, contentProperties.get(SUMMARY));

if (noteMetadataItem != null) {
noteMetadataItem.setProperties(noteProperties);
metadataService.updateMetadataItem(noteMetadataItem, creatorId);
} else {
metadataService.createMetadataItem(noteMetadataObject, NOTES_METADATA_KEY, noteProperties, creatorId);
}
processedContentPagesPropertiesCount++;
LOG.info("ContentArticlePropertiesUpgrade: Processed content page properties: {}/{}",
totalContentPagesPropertiesCount,
processedContentPagesPropertiesCount);
}
}
}
} catch (Exception e) {
LOG.error("An error occurred while Migrating content pages properties:", e);
}
notMigratedContentPagesPropertiesCount = totalContentPagesPropertiesCount - processedContentPagesPropertiesCount;
if (notMigratedContentPagesPropertiesCount == 0) {
LOG.info("End ContentArticlePropertiesUpgrade successful migration: total={} succeeded={} error={}. It took {} ms.",
totalContentPagesPropertiesCount,
processedContentPagesPropertiesCount,
notMigratedContentPagesPropertiesCount,
(System.currentTimeMillis() - startupTime));
} else {
LOG.warn("End ContentArticlePropertiesUpgrade with some errors: total={} succeeded={} error={}. It took {} ms."
+ " The not migrated news articles will be processed again next startup.",
totalContentPagesPropertiesCount,
processedContentPagesPropertiesCount,
notMigratedContentPagesPropertiesCount,
(System.currentTimeMillis() - startupTime));
throw new IllegalStateException("Some content page properties weren't migrated successfully. It will be re-attempted next startup");
}
}

private MetadataFilter getMetadataFilter() {
MetadataFilter metadataFilter = new MetadataFilter();
metadataFilter.setMetadataName(NEWS_METADATA_NAME);
metadataFilter.setMetadataTypeName(NEWS_METADATA_NAME);
return metadataFilter;
}

private NoteMetadataObject buildNoteMetadataObject(Page note, String lang, String objectType) {
Space space = spaceService.getSpaceByGroupId(note.getWikiOwner());
long spaceId = space != null ? Long.parseLong(space.getId()) : 0L;
String noteId = String.valueOf(note.getId());
noteId = lang != null ? noteId + "-" + lang : noteId;
return new NoteMetadataObject(objectType, noteId, note.getParentPageId(), spaceId);
}

private MetadataItem getNoteMetadataItem(Page note, String lang, String objectType) {
NoteMetadataObject noteMetadataObject = buildNoteMetadataObject(note, lang, objectType);
return metadataService.getMetadataItemsByMetadataAndObject(NOTES_METADATA_KEY, noteMetadataObject)
.stream()
.findFirst()
.orElse(null);
}

}
33 changes: 33 additions & 0 deletions data-upgrade-news/src/main/resources/conf/portal/configuration.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,39 @@
</value-param>
</init-params>
</component-plugin>
<component-plugin profiles="content">
<name>ContentArticlePropertiesUpgrade</name>
<set-method>addUpgradePlugin</set-method>
<type>org.exoplatform.news.upgrade.ContentArticlePropertiesUpgrade</type>
<description>Migrate content properties from content to note</description>
<init-params>
<value-param>
<name>product.group.id</name>
<description>The groupId of the product</description>
<value>org.exoplatform.platform</value>
</value-param>
<value-param>
<name>plugin.execution.order</name>
<description>The plugin execution order</description>
<value>1</value>
</value-param>
<value-param>
<name>plugin.upgrade.execute.once</name>
<description>Execute this upgrade plugin only once</description>
<value>true</value>
</value-param>
<value-param>
<name>plugin.upgrade.async.execution</name>
<description>The plugin will be executed in an asynchronous mode</description>
<value>true</value>
</value-param>
<value-param>
<name>plugin.upgrade.target.version</name>
<description>Target version of the plugin</description>
<value>7.0.0</value>
</value-param>
</init-params>
</component-plugin>
</external-component-plugins>

</configuration>
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package org.exoplatform.news.upgrade;

import io.meeds.notes.model.NoteMetadataObject;
import org.exoplatform.container.xml.InitParams;
import org.exoplatform.container.xml.ValueParam;
import org.exoplatform.portal.config.UserACL;
import org.exoplatform.social.core.identity.model.Identity;
import org.exoplatform.social.core.manager.IdentityManager;
import org.exoplatform.social.core.space.spi.SpaceService;
import org.exoplatform.social.metadata.MetadataService;
import org.exoplatform.social.metadata.model.MetadataItem;
import org.exoplatform.social.metadata.model.MetadataKey;
import org.exoplatform.wiki.model.DraftPage;
import org.exoplatform.wiki.model.Page;
import org.exoplatform.wiki.model.PageVersion;
import org.exoplatform.wiki.service.NoteService;
import org.exoplatform.wiki.utils.Utils;
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.ArrayList;
import java.util.List;
import java.util.Map;

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

@RunWith(MockitoJUnitRunner.class)
public class ContentArticlePropertiesUpgradeTest {

@Mock
private NoteService noteService;

@Mock
private MetadataService metadataService;

@Mock
private IdentityManager identityManager;

@Mock
private SpaceService spaceService;

@Mock
private UserACL userACL;

private ContentArticlePropertiesUpgrade contentArticlePropertiesUpgrade;

private static final MetadataKey NOTES_METADATA_KEY = new MetadataKey("notes", Utils.NOTES_METADATA_OBJECT_TYPE, 0);

private static final String ILLUSTRATION_ID = "illustrationId";

private static final String SUMMARY = "summary";

@Before
public void setUp() {
InitParams initParams = new InitParams();
ValueParam valueParam = new ValueParam();
valueParam.setName("product.group.id");
valueParam.setValue("org.exoplatform.platform");
initParams.addParameter(valueParam);
this.contentArticlePropertiesUpgrade = new ContentArticlePropertiesUpgrade(initParams,
noteService,
metadataService,
identityManager,
spaceService,
userACL);
}

@Test
public void processUpgrade() throws Exception {
MetadataItem page = new MetadataItem();
MetadataItem draftOfPage = new MetadataItem();
MetadataItem draft = new MetadataItem();
page.setId(1L);
page.setObjectType("newsPageVersion");
page.setObjectId("1");
page.setProperties(Map.of(ILLUSTRATION_ID, "1", SUMMARY, "test summary"));

draftOfPage.setId(2L);
draftOfPage.setObjectType("newsLatestDraftPage");
draftOfPage.setObjectId("2");
draftOfPage.setProperties(Map.of(ILLUSTRATION_ID, "2", SUMMARY, "test summary"));

draft.setId(1L);
draft.setObjectId("3");
draft.setObjectType("newsDraftPage");
draft.setProperties(Map.of(ILLUSTRATION_ID, "3", SUMMARY, "test summary"));

Page parentPage = new Page();
parentPage.setId("2");
parentPage.setAuthor("user");
PageVersion pageVersion = new PageVersion();
pageVersion.setId("1");
pageVersion.setParent(parentPage);
pageVersion.setAuthor("user");

DraftPage draftOfExistingPage = new DraftPage();
draftOfExistingPage.setId("2");
draftOfExistingPage.setAuthor("user");

DraftPage draftPage = new DraftPage();
draftPage.setId("3");
draftPage.setAuthor("user");

MetadataItem notePage = new MetadataItem();
notePage.setId(5L);
notePage.setObjectType("notePage");
notePage.setObjectId("2");

List<MetadataItem> metadataItems = List.of(page, draftOfPage, draft);
Identity identity = mock(Identity.class);
when(metadataService.getMetadataItemsByFilter(any(), anyLong(), anyLong())).thenReturn(metadataItems);
when(noteService.getPageVersionById(anyLong())).thenReturn(pageVersion);
when(noteService.getDraftNoteById(anyString(), anyString())).thenReturn(draftOfExistingPage, draftPage);
when(identityManager.getOrCreateUserIdentity(anyString())).thenReturn(identity);
when(metadataService.getMetadataItemsByMetadataAndObject(NOTES_METADATA_KEY,
new NoteMetadataObject("noteDraftPage", "3", null, 0L)))
.thenReturn(new ArrayList<>());
when(metadataService.getMetadataItemsByMetadataAndObject(NOTES_METADATA_KEY,
new NoteMetadataObject("noteDraftPage", "2", null, 0L)))
.thenReturn(new ArrayList<>());
when(metadataService.getMetadataItemsByMetadataAndObject(NOTES_METADATA_KEY,
new NoteMetadataObject("notePage", "2", null, 0L)))
.thenReturn(List.of(notePage));

when(userACL.getSuperUser()).thenReturn("root");
when(identity.getId()).thenReturn("1");
contentArticlePropertiesUpgrade.processUpgrade(null, null);

verify(metadataService, times(1)).updateMetadataItem(any(), anyLong());
verify(metadataService, times(2)).createMetadataItem(any(), any(), any(), anyLong());
}
}