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

#28894 Create Endpoint to calculate Hierarchy #29142

Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.dotmarketing.portlets.categories.business.CategoryAPI;
import com.dotmarketing.portlets.categories.business.PaginatedCategories;
import com.dotmarketing.portlets.categories.model.Category;
import com.dotmarketing.portlets.categories.model.HierarchyShortCategory;
import com.dotmarketing.util.ActivityLogger;
import com.dotmarketing.util.Logger;
import com.dotmarketing.util.PageMode;
Expand Down Expand Up @@ -276,6 +277,21 @@ public final Response getChildren(@Context final HttpServletRequest httpRequest,
return response;
}

@GET
@Path(("/hierarchy"))
@JSONP
@NoCache
@Produces({MediaType.APPLICATION_JSON})
public final Response getHierarchy(@Context final HttpServletRequest httpRequest,
dsilvam marked this conversation as resolved.
Show resolved Hide resolved
@Context final HttpServletResponse httpResponse,
final CategoryInodesForm form) throws DotDataException, DotSecurityException {

webResource.init(null, httpRequest, httpResponse, true, null);

freddyDOTCMS marked this conversation as resolved.
Show resolved Hide resolved
return Response.ok(
new HierarchyShortCategoriesResponseView(categoryAPI.findHierarchy(form.getInodes()))).build();
}

/**
* Lookup operation. Categories can be retrieved by category id or key
* @param httpRequest
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.dotcms.rest.api.v1.categories;

import com.dotcms.repackage.javax.validation.constraints.NotNull;
import com.dotcms.rest.api.Validated;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import java.util.List;

/**
* Category Input Form
*
* @author Hassan Mustafa Baig
dsilvam marked this conversation as resolved.
Show resolved Hide resolved
*/
@JsonDeserialize(builder = CategoryInodesForm.Builder.class)
public class CategoryInodesForm extends Validated {

private List<String> inodes;

private CategoryInodesForm(final Builder builder) {

this.inodes = builder.inodes;
}

public List<String> getInodes() {
return inodes;
}

public static final class Builder {

@JsonProperty
private List<String> inodes;

public void setInodes(List<String> inodes) {
this.inodes = inodes;
}

public CategoryInodesForm build() {

return new CategoryInodesForm(this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.dotcms.rest.api.v1.categories;

import com.dotcms.experiments.model.Experiment;
import com.dotcms.rest.ResponseEntityView;
import com.dotmarketing.portlets.categories.model.HierarchyShortCategory;

import java.util.List;

public class HierarchyShortCategoriesResponseView extends ResponseEntityView<List<HierarchyShortCategory>> {
public HierarchyShortCategoriesResponseView(final List<HierarchyShortCategory> categories) {
super(categories);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.exception.DotSecurityException;
import com.dotmarketing.portlets.categories.model.Category;
import com.dotmarketing.portlets.categories.model.HierarchyShortCategory;
import com.dotmarketing.portlets.contentlet.model.Contentlet;
import com.liferay.portal.model.User;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
/**
Expand Down Expand Up @@ -496,4 +499,38 @@ Category findByVariable(final String variable, final User user,
*/
PaginatedCategories findAll(final CategorySearchCriteria searchCriteria, final User user, boolean respectFrontendRoles)
throws DotDataException, DotSecurityException;

/**
* Find Categories by inodes and calculate its Hierarchy.
*
* For Example if you have the follows categories:
*
*
* | Inode | Name | key |Parent |
* |--------|-------------|-------------|----------------|
* | 1 | Top Category| top | null |
* | 2 | Child | child | Top Category |
* | 3 | Grand Child | grand_child | Child |
*
* And you find by inode 3 then you got:
dsilvam marked this conversation as resolved.
Show resolved Hide resolved
*
* Inode: 3
* parentList <code>[
* {
* 'categoryName':'Top Category',
* 'key': 'top',
* 'inode': '1'
* },
* {
* 'categoryName':'Child',
* 'key': 'child',
* 'inode': '2'
* }
* ]</code>
*
* @param inodes List of inodes to search
* @return
* @throws DotDataException
*/
List<HierarchyShortCategory> findHierarchy(final Collection<String> inodes) throws DotDataException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.exception.DotSecurityException;
import com.dotmarketing.portlets.categories.model.Category;
import com.dotmarketing.portlets.categories.model.HierarchyShortCategory;
import com.dotmarketing.portlets.contentlet.model.Contentlet;
import com.dotmarketing.util.ActivityLogger;
import com.dotmarketing.util.InodeUtils;
Expand All @@ -25,14 +26,8 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.liferay.portal.model.User;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

import java.util.*;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -978,4 +973,17 @@ public PaginatedCategories findAll(final CategorySearchCriteria searchCriteria,
return getCategoriesSubList(searchCriteria.offset, searchCriteria.limit, categories, null);
}

/**
* Default implementation of {@link CategoryAPI}
*
* @param inodes List of inodes to search
* @return
*
* @throws DotDataException
*/
@CloseDBIfOpened
@Override
public List<HierarchyShortCategory> findHierarchy(final Collection<String> inodes) throws DotDataException {
return categoryFactory.findHierarchy(inodes);
dsilvam marked this conversation as resolved.
Show resolved Hide resolved
freddyDOTCMS marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.dotmarketing.portlets.categories.business;


import java.util.Collection;
import java.util.List;

import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.portlets.categories.model.Category;
import com.dotmarketing.portlets.categories.model.HierarchedCategory;
import com.dotmarketing.portlets.categories.model.HierarchyShortCategory;


/**
Expand Down Expand Up @@ -286,4 +288,38 @@ public abstract class CategoryFactory {
* @return List of Category filteredx
*/
public abstract List<HierarchedCategory> findAll(final CategorySearchCriteria searchCriteria) throws DotDataException;

/**
* Find Categories by inodes and calculate its Hierarchy.
*
* For Example if you have the follows categories:
*
*
* | Inode | Name | key |Parent |
* |--------|-------------|-------------|----------------|
* | 1 | Top Category| top | null |
* | 2 | Child | child | Top Category |
* | 3 | Grand Child | grand_child | Child |
*
* And you find by inode 3 then you got:
dsilvam marked this conversation as resolved.
Show resolved Hide resolved
*
* Inode: 3
* parentList <code>[
* {
* 'categoryName':'Top Category',
* 'key': 'top',
* 'inode': '1'
* },
* {
* 'categoryName':'Child',
* 'key': 'child',
* 'inode': '2'
* }
* ]</code>
*
* @param inodes List of inodes to search
* @return
* @throws DotDataException
*/
public abstract List<HierarchyShortCategory> findHierarchy(final Collection<String> inodes) throws DotDataException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,24 @@
import com.dotmarketing.common.util.SQLUtil;
import com.dotmarketing.db.DbConnectionFactory;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.exception.DotRuntimeException;
import com.dotmarketing.factories.TreeFactory;
import com.dotmarketing.portlets.categories.model.Category;
import com.dotmarketing.portlets.categories.model.HierarchedCategory;
import com.dotmarketing.portlets.categories.model.HierarchyShortCategory;
import com.dotmarketing.portlets.categories.model.ShortCategory;
import com.dotmarketing.util.InodeUtils;
import com.dotmarketing.util.Logger;
import com.dotmarketing.util.UtilMethods;
import com.dotmarketing.util.VelocityUtil;
import com.liferay.util.StringPool;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.io.Serializable;
import java.sql.*;
import java.util.ArrayList;
import java.util.*;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -633,22 +633,12 @@ private List<HierarchedCategory> convertForHierarchedCategories(final List<Map<S
try {
if (row !=null && row.get("path") != null ) {
final String parentsASJsonArray = "[" + row.get("path") + "]";

final List<ShortCategory> parentList = ((List<Map<String, String>>) JsonUtil.getObjectFromJson(parentsASJsonArray, List.class))
.stream()
.map(map -> new ShortCategory.Builder()
.setCategoryName(map.get("categoryName"))
.setKey(map.get("key"))
.setInode(map.get(INODE))
.build()
)
.collect(Collectors.toList());

final List<ShortCategory> parentList = getShortCategories(parentsASJsonArray);
category.setParentList(parentList.subList(0, parentList.size() - 1));
}

categories.add(category);
} catch (IOException e) {
} catch ( Exception e) {
Logger.warn(CategoryFactoryImpl.class, e::getMessage);
}
}
Expand All @@ -657,6 +647,23 @@ private List<HierarchedCategory> convertForHierarchedCategories(final List<Map<S
return categories;
}


private static List<ShortCategory> getShortCategories(String parentsASJsonArray) {
freddyDOTCMS marked this conversation as resolved.
Show resolved Hide resolved
try {
return ((List<Map<String, String>>) JsonUtil.getObjectFromJson(parentsASJsonArray, List.class))
.stream()
.map(map -> new ShortCategory.Builder()
.setCategoryName(map.get("categoryName"))
.setKey(map.get("key"))
.setInode(map.get(INODE))
.build()
)
.collect(Collectors.toList());
} catch (IOException e) {
throw new DotRuntimeException(e);
}
}

private Category convertForCategory(final Map<String, Object> sqlResult) {
return convertForCategory(sqlResult, Category.class);
}
Expand Down Expand Up @@ -949,4 +956,50 @@ private void updateCache(List<? extends Category> categories) throws DotDataExce
}
}

/**
* Default implementation of {@link CategoryFactory#findHierarchy(Collection)}
*
* @param inodes list of inodes to search
* @return
* @throws DotDataException
*/
@Override
public List<HierarchyShortCategory> findHierarchy(final Collection<String> inodes) throws DotDataException {
final String queryTemplate = "WITH RECURSIVE CategoryHierarchy AS ( " +
"SELECT " +
"c.inode," +
"c.inode AS root_inode," +
"1 AS level," +
"json_build_object('inode', c.inode, 'categoryName', c.category_name, 'key', c.category_key)::varchar AS path " +
"FROM Category c " +
"WHERE c.inode IN (%s) " +
"UNION ALL " +
"SELECT " +
"c.inode, " +
"ch.root_inode AS root_inode, " +
"ch.level + 1 AS level, " +
"CONCAT(json_build_object('inode', c.inode, 'categoryName', c.category_name, 'key', c.category_key)::varchar, ',', ch.path) AS path " +
"FROM Category c JOIN tree t ON c.inode = t.parent JOIN CategoryHierarchy ch ON t.child = ch.inode " +
")," +
"MaxLevels AS (SELECT root_inode, MAX(level) AS max_level FROM CategoryHierarchy GROUP BY root_inode) " +
"SELECT " +
"ch.root_inode as inode, " +
"CONCAT('[', path, ']')::jsonb as path " +
"FROM CategoryHierarchy ch JOIN MaxLevels ml ON ch.root_inode = ml.root_inode AND ch.level = ml.max_level;";


final DotConnect dc = new DotConnect()
.setSQL(String.format(queryTemplate, DotConnect.createParametersPlaceholder(inodes.size())));

inodes.forEach(dc::addParam);

return dc.loadObjectResults().stream().map(row -> {
final List<ShortCategory> parentList = getShortCategories(row.get("path").toString());

return new HierarchyShortCategory(row.get(INODE).toString(), parentList.subList(0, parentList.size() - 1));
}).collect(Collectors.toList());
}



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.dotmarketing.portlets.categories.model;

import java.util.List;

/**
* Represents a HierarchyCategory but just include the parentList and the inode
*/
public class HierarchyShortCategory {
private String inode;
freddyDOTCMS marked this conversation as resolved.
Show resolved Hide resolved
private List<ShortCategory> parentList;

public HierarchyShortCategory(final String inode, final List<ShortCategory> parentList) {
this.inode = inode;
this.parentList = parentList;
}

public String getInode() {
return inode;
}

public List<ShortCategory> getParentList() {
return parentList;
}
}
Loading
Loading