From f6577087fe8ab253a2a411600e73e11bea0c666b Mon Sep 17 00:00:00 2001 From: Helmi Akermi Date: Wed, 31 Jul 2024 15:37:00 +0100 Subject: [PATCH] feat: Add an upgrade plugin for content pages properties migration - EXO-73181 - Meeds-io/MIPs#128 Add an upgrade plugin for content pages properties(summary/illustrationId) migration --- .../ContentArticlePropertiesUpgrade.java | 194 ++++++++++++++++++ .../resources/conf/portal/configuration.xml | 33 +++ .../ContentArticlePropertiesUpgradeTest.java | 137 +++++++++++++ 3 files changed, 364 insertions(+) create mode 100644 data-upgrade-news/src/main/java/org/exoplatform/news/upgrade/ContentArticlePropertiesUpgrade.java create mode 100644 data-upgrade-news/src/test/java/org/exoplatform/news/upgrade/ContentArticlePropertiesUpgradeTest.java diff --git a/data-upgrade-news/src/main/java/org/exoplatform/news/upgrade/ContentArticlePropertiesUpgrade.java b/data-upgrade-news/src/main/java/org/exoplatform/news/upgrade/ContentArticlePropertiesUpgrade.java new file mode 100644 index 000000000..3a8ffbbf3 --- /dev/null +++ b/data-upgrade-news/src/main/java/org/exoplatform/news/upgrade/ContentArticlePropertiesUpgrade.java @@ -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 . + */ +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 metadataItems = metadataService.getMetadataItemsByFilter(metadataFilter, 0, 0); + totalContentPagesPropertiesCount = metadataItems.size(); + for (MetadataItem metadataItem : metadataItems) { + if (metadataItem != null && !MapUtils.isEmpty(metadataItem.getProperties())) { + Map 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 noteProperties = new HashMap<>(); + long creatorId = Long.parseLong(identityManager.getOrCreateUserIdentity(page.getAuthor()).getId()); + 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())); + } + 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); + } + +} diff --git a/data-upgrade-news/src/main/resources/conf/portal/configuration.xml b/data-upgrade-news/src/main/resources/conf/portal/configuration.xml index a47efa764..53de8dd9e 100644 --- a/data-upgrade-news/src/main/resources/conf/portal/configuration.xml +++ b/data-upgrade-news/src/main/resources/conf/portal/configuration.xml @@ -48,6 +48,39 @@ + + ContentArticlePropertiesUpgrade + addUpgradePlugin + org.exoplatform.news.upgrade.ContentArticlePropertiesUpgrade + Migrate content properties from content to note + + + product.group.id + The groupId of the product + org.exoplatform.platform + + + plugin.execution.order + The plugin execution order + 1 + + + plugin.upgrade.execute.once + Execute this upgrade plugin only once + true + + + plugin.upgrade.async.execution + The plugin will be executed in an asynchronous mode + true + + + plugin.upgrade.target.version + Target version of the plugin + 7.0.0 + + + diff --git a/data-upgrade-news/src/test/java/org/exoplatform/news/upgrade/ContentArticlePropertiesUpgradeTest.java b/data-upgrade-news/src/test/java/org/exoplatform/news/upgrade/ContentArticlePropertiesUpgradeTest.java new file mode 100644 index 000000000..6764072f4 --- /dev/null +++ b/data-upgrade-news/src/test/java/org/exoplatform/news/upgrade/ContentArticlePropertiesUpgradeTest.java @@ -0,0 +1,137 @@ +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.junit.Assert.*; +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() throws Exception { + 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 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()); + } +}