Skip to content

Commit

Permalink
#30296: Creation of upgrade task to replace languages portlets by lo…
Browse files Browse the repository at this point in the history
…cales ones. (#30340)
  • Loading branch information
victoralfaro-dotcms authored Oct 16, 2024
1 parent 167058a commit 11a47ce
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class Task240530AddDotAIPortletToLayout implements StartupTask {
@Override
public boolean forceRun() {

// first we get all layouts we have maintenance portelt
// first we get all layouts we have maintenance portlet
final List<Object> apiPlaygroundLayouts = getAPIPlaygroundLayouts();

// then check if all layouts which contains API Playground also have dotAI
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.dotmarketing.startup.runonce;

import com.dotmarketing.business.CacheLocator;
import com.dotmarketing.common.db.DotConnect;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.exception.DotRuntimeException;
import com.dotmarketing.startup.StartupTask;
import com.dotmarketing.util.UUIDUtil;
import io.vavr.control.Try;

import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
* Adds the dotAI portlet to all layouts which have API Playground portlet too, if it does not already exist.
* @author vico
*/
public class Task241015ReplaceLanguagesWithLocalesPortlet implements StartupTask {

public static final String LANGUAGES_PORTLET_ID = "languages";
public static final String LOCALES_PORTLET_ID = "locales";
public static final String WORKFLOW_PORTLET_ID = "workflow-schemes";

/**
* Determines if the task should be forced to run.
*
* @return true if no locales portlet is found in any layout, false otherwise.
*/
@Override
public boolean forceRun() {
// if no locales found then flag it as true
return getLayoutPortletsByPortletId(LOCALES_PORTLET_ID).isEmpty();
}

/**
* Executes the upgrade task.
* Adds the locales portlet to layouts containing the languages portlet or the workflow portlet.
* Replaces the languages portlet with the locales portlet.
* Clears the layout cache.
*
* @throws DotDataException if there is an error accessing the database.
* @throws DotRuntimeException if there is a runtime error during execution.
*/
@Override
public void executeUpgrade() throws DotDataException, DotRuntimeException {
final List<Map<String, Object>> languagesLayoutPortlets = getLayoutPortletsByPortletId(LANGUAGES_PORTLET_ID);

if (languagesLayoutPortlets.isEmpty()) {
final List<Map<String, Object>> workflowLayoutPortlets = getLayoutPortletsByPortletId(WORKFLOW_PORTLET_ID);
if (!workflowLayoutPortlets.isEmpty()) {
final Map<String, Object> row = workflowLayoutPortlets.get(0);
final String layoutId = (String) row.get("layout_id");
final int portletOrder = Optional.ofNullable((Integer) row.get("portlet_order")).orElse(0) + 1;
insertLocalesPortlet(layoutId, portletOrder);
}
} else {
languagesLayoutPortlets.forEach(this::replaceLanguage);
}

CacheLocator.getLayoutCache().clearCache();
}

private void replaceLanguage(final Map<String, Object> row) {
final String layoutId = (String) row.get("layout_id");
final int portletOrder = Optional.ofNullable((Integer) row.get("portlet_order")).orElse(1);
insertLocalesPortlet(layoutId, portletOrder);

final String id = (String) row.get("id");
deleteLanguagesPortlet(id);
}

private void deleteLanguagesPortlet(final String id) {
Try.run(() -> new DotConnect()
.executeStatement(String.format("DELETE FROM cms_layouts_portlets WHERE id = '%s'", id)))
.getOrElseThrow(ex -> new RuntimeException(ex));
}

private static void insertLocalesPortlet(final String layoutId, final int portletOrder) {
Try.run(() -> new DotConnect()
.setSQL("INSERT INTO cms_layouts_portlets(id, layout_id, portlet_id, portlet_order)" +
" VALUES (?, ?, ?, ?)")
.addParam(UUIDUtil.uuid())
.addParam(layoutId)
.addParam(LOCALES_PORTLET_ID)
.addParam(portletOrder)
.loadResult())
.getOrElseThrow(ex -> new RuntimeException(ex));
}

private List<Map<String, Object>> getLayoutPortletsByPortletId(final String portletId) {
return Try.of(
() -> new DotConnect()
.setSQL("SELECT * FROM cms_layouts_portlets WHERE portlet_id = ?")
.addParam(portletId)
.loadObjectResults())
.getOrElse(List.of());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -351,15 +351,12 @@ public static List<Class<?>> getStartupRunOnceTaskClasses() {
.add(Task241009CreatePostgresJobQueueTables.class)
.add(Task241013RemoveFullPathLcColumnFromIdentifier.class)
.add(Task241014AddTemplateValueOnContentletIndex.class)
.add(Task241015ReplaceLanguagesWithLocalesPortlet.class)
.build();
return ret.stream().sorted(classNameComparator).collect(Collectors.toList());
}

final static private Comparator<Class<?>> classNameComparator = new Comparator<Class<?>>() {
public int compare(Class<?> o1, Class<?> o2) {
return o1.getName().compareTo(o2.getName());
}
};
private static final Comparator<Class<?>> classNameComparator = Comparator.comparing(Class::getName);

/**
* Returns list of tasks that are run <b>every time</b> that dotCMS starts
Expand Down
4 changes: 3 additions & 1 deletion dotcms-integration/src/test/java/com/dotcms/MainSuite2b.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@
import com.dotmarketing.startup.runonce.Task240530AddDotAIPortletToLayoutTest;
import com.dotmarketing.startup.runonce.Task240606AddVariableColumnToWorkflowTest;
import com.dotmarketing.startup.runonce.Task241009CreatePostgresJobQueueTablesTest;
import com.dotmarketing.startup.runonce.Task241015ReplaceLanguagesWithLocalesPortletTest;
import com.dotmarketing.startup.runonce.Task241013RemoveFullPathLcColumnFromIdentifierTest;
import com.dotmarketing.util.ConfigUtilsTest;
import com.dotmarketing.util.ITConfigTest;
Expand Down Expand Up @@ -386,7 +387,8 @@
LegacyJSONObjectRenderTest.class,
Task241013RemoveFullPathLcColumnFromIdentifierTest.class,
Task241009CreatePostgresJobQueueTablesTest.class,
Task241013RemoveFullPathLcColumnFromIdentifierTest.class
Task241013RemoveFullPathLcColumnFromIdentifierTest.class,
Task241015ReplaceLanguagesWithLocalesPortletTest.class
})

public class MainSuite2b {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.dotmarketing.startup.runonce;

import com.dotcms.util.IntegrationTestInitService;
import com.dotmarketing.common.db.DotConnect;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.util.UUIDUtil;
import org.junit.BeforeClass;
import org.junit.Test;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class Task241015ReplaceLanguagesWithLocalesPortletTest {

@BeforeClass
public static void prepare() throws Exception {
// Setting web app environment
IntegrationTestInitService.getInstance().init();
}

/**
* Given the locales portlets are inserted
* When the upgrade task is executed
* Then the locales portlets should be added to the layout
* And the task should not need to be forced to run
*
* @throws SQLException if there is an SQL error
* @throws DotDataException if there is a data access error
*/
@Test
public void test_upgradeTask_success() throws SQLException, DotDataException {
final DotConnect dotConnect = new DotConnect();
final Task241015ReplaceLanguagesWithLocalesPortlet task = new Task241015ReplaceLanguagesWithLocalesPortlet();

insertLocalesPortlets(dotConnect);
assertFalse(task.forceRun());

deleteAnyLocalesPortlets(dotConnect);
assertTrue(task.forceRun());

task.executeUpgrade();
assertFalse(task.forceRun());
}

/**
* Scenario: Upgrade task execution when no languages portlets are present
*
* Given the locales portlets are not present
* When the upgrade task is executed
* Then the locales portlets should be added to the layout
* And the task should not need to be forced to run
*
* @throws SQLException if there is an SQL error
* @throws DotDataException if there is a data access error
*/
@Test
public void test_upgradeTask_noLanguages_success() throws SQLException, DotDataException {
final DotConnect dotConnect = new DotConnect();
final Task241015ReplaceLanguagesWithLocalesPortlet task = new Task241015ReplaceLanguagesWithLocalesPortlet();

assertFalse(task.forceRun());

deleteAnyLanguagesPortlets(dotConnect);
deleteAnyLocalesPortlets(dotConnect);
assertTrue(task.forceRun());

task.executeUpgrade();
assertFalse(task.forceRun());
}

private void insertLocalesPortlets(final DotConnect dotConnect) throws DotDataException {
final List<Map<String, Object>> rows = dotConnect
.setSQL("SELECT layout_id, portlet_order" +
" FROM cms_layouts_portlets" +
" WHERE portlet_id = ?" +
" ORDER BY portlet_order" +
" DESC LIMIT 1")
.addParam(Task241015ReplaceLanguagesWithLocalesPortlet.LANGUAGES_PORTLET_ID)
.loadObjectResults();
if (rows.isEmpty()) {
return;
}

final Map<String, Object> row = rows.get(0);
dotConnect
.setSQL("INSERT INTO cms_layouts_portlets(id, layout_id, portlet_id, portlet_order)" +
" VALUES(?, ?, ?, ?)")
.addParam(UUIDUtil.uuid())
.addParam(row.get("layout_id"))
.addParam(Task241015ReplaceLanguagesWithLocalesPortlet.LOCALES_PORTLET_ID)
.addParam(((Integer) row.get("portlet_order")) + 1)
.loadResult();
}

private void deletePortlets(final DotConnect dotConnect, final String portletId) throws SQLException {
dotConnect
.executeStatement(String.format(
"DELETE FROM cms_layouts_portlets WHERE portlet_id = '%s'",
portletId));
}

private void deleteAnyLocalesPortlets(final DotConnect dotConnect) throws SQLException {
deletePortlets(dotConnect, Task241015ReplaceLanguagesWithLocalesPortlet.LOCALES_PORTLET_ID);
}

private void deleteAnyLanguagesPortlets(final DotConnect dotConnect) throws SQLException {
deletePortlets(dotConnect, Task241015ReplaceLanguagesWithLocalesPortlet.LANGUAGES_PORTLET_ID);
}

}

0 comments on commit 11a47ce

Please sign in to comment.