Skip to content

Commit

Permalink
feat: Add an upgrade plugin for content pages properties migration - E…
Browse files Browse the repository at this point in the history
…XO-73181 - Meeds-io/MIPs#128 (#240)

Add an upgrade plugin for content pages properties(summary/illustrationId) migration
  • Loading branch information
hakermi committed Aug 1, 2024
1 parent 565fba0 commit 8e5ce7c
Show file tree
Hide file tree
Showing 3 changed files with 384 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/*
* 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;
int ignoredContentPagesPropertiesCount = 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);

if (noteMetadataItem != null) {
LOG.info("ContentArticlePropertiesUpgrade: Content page properties already migrated");
ignoredContentPagesPropertiesCount++;
} else {
Map<String, String> noteProperties = new HashMap<>();
long creatorId = Long.parseLong(identityManager.getOrCreateUserIdentity(page.getAuthor()).getId());

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()));
}
noteProperties.put(SUMMARY, contentProperties.get(SUMMARY));
metadataService.createMetadataItem(noteMetadataObject, NOTES_METADATA_KEY, noteProperties, creatorId);
}
} else {
ignoredContentPagesPropertiesCount++;
LOG.warn("ContentArticlePropertiesUpgrade: Content page properties ignored due to data inconsistency: "
+ "page exists: {}, page name: {}",
"ObjectType: {}",
"Page Id: {}",
"Page author: {}",
page != null,
page != null ? page.getName() : null,
objectType,
page != null ? page.getId() : null,
null);
}
}
processedContentPagesPropertiesCount++;
LOG.info("ContentArticlePropertiesUpgrade: Processed content page properties: {}/{}",
processedContentPagesPropertiesCount,
totalContentPagesPropertiesCount);
}
} 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={} ignored={} error={}. It took {} ms.",
totalContentPagesPropertiesCount,
processedContentPagesPropertiesCount,
ignoredContentPagesPropertiesCount,
notMigratedContentPagesPropertiesCount,
(System.currentTimeMillis() - startupTime));
} else {
LOG.warn("End ContentArticlePropertiesUpgrade with some errors: total={} succeeded={} ignored={} error={}. It took {} ms."
+ " The not migrated news articles will be processed again next startup.",
totalContentPagesPropertiesCount,
processedContentPagesPropertiesCount,
ignoredContentPagesPropertiesCount,
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>ContentArticlePropertiesUpgrade2</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,144 @@
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 draftOfPage2 = 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"));

draftOfPage2.setId(4L);
draftOfPage2.setObjectType("newsLatestDraftPage");
draftOfPage2.setObjectId("6");
draftOfPage2.setProperties(Map.of(ILLUSTRATION_ID, "4", 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 draftOfExistingPage2 = new DraftPage();
draftOfExistingPage2.setId("6");

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, draftOfPage2, 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, draftOfExistingPage2, 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(2)).createMetadataItem(any(), any(), any(), anyLong());
}
}

0 comments on commit 8e5ce7c

Please sign in to comment.