com.sdl.dxa.tridion.navigation.GraphQLDynamicNavigationModelProvider Maven / Gradle / Ivy
The newest version!
package com.sdl.dxa.tridion.navigation;
import com.sdl.dxa.api.datamodel.model.SitemapItemModelData;
import com.sdl.dxa.api.datamodel.model.TaxonomyNodeModelData;
import com.sdl.dxa.common.dto.DepthCounter;
import com.sdl.dxa.common.dto.SitemapRequestDto;
import com.sdl.dxa.tridion.navigation.dynamic.NavigationModelProvider;
import com.sdl.dxa.tridion.navigation.dynamic.OnDemandNavigationModelProvider;
import com.sdl.dxa.tridion.pcaclient.ApiClientProvider;
import com.sdl.dxa.tridion.pcaclient.GraphQLUtils;
import com.sdl.web.pca.client.contentmodel.ContextData;
import com.sdl.web.pca.client.contentmodel.generated.Ancestor;
import com.sdl.web.pca.client.contentmodel.generated.PageSitemapItem;
import com.sdl.web.pca.client.contentmodel.generated.SitemapItem;
import com.sdl.web.pca.client.contentmodel.generated.TaxonomySitemapItem;
import com.sdl.web.pca.client.exception.ApiClientException;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static com.sdl.dxa.tridion.common.ContextDataCreator.createContextData;
import static com.sdl.web.pca.client.contentmodel.generated.Ancestor.NONE;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
@Slf4j
@Service
@Profile("!cil.providers.active")
@Primary
public class GraphQLDynamicNavigationModelProvider implements NavigationModelProvider,
OnDemandNavigationModelProvider {
private final int defaultDescendantDepth;
private final ApiClientProvider provider;
@Autowired
public GraphQLDynamicNavigationModelProvider(
ApiClientProvider provider,
@Value("${dxa.pca.dynamic.navigation.sitemap.descendant.depth:10}") int defaultDescendantDepth) {
this.provider = provider;
this.defaultDescendantDepth = defaultDescendantDepth;
}
@Override
public Optional getNavigationModel(@NotNull SitemapRequestDto requestDto) {
try {
Optional navigation = getNavigationModelInternal(requestDto);
return navigation.map(item -> (TaxonomyNodeModelData) convert(item));
} catch (ApiClientException e) {
log.warn("Cannot find/load/convert dynamic navigation in the PCA for the request " + requestDto, e);
return Optional.empty();
}
}
private Optional getNavigationModelInternal(@NotNull SitemapRequestDto request) {
int depth = getDepth(request);
ContextData contextData = createContextData(request.getClaims());
com.sdl.web.pca.client.ApiClient client = provider.getClient();
TaxonomySitemapItem taxonomySitemapItem = client.getSitemap(GraphQLUtils.convertUriToGraphQLContentNamespace(request.getUriType()),
request.getLocalizationId(),
depth,
contextData);
if (taxonomySitemapItem == null) {
return Optional.empty();
}
List leafNodes = getLeafNodes(taxonomySitemapItem);
while (leafNodes.size() > 0) {
TaxonomySitemapItem node = leafNodes.remove(0);
if (node.getHasChildNodes()) {
TaxonomySitemapItem[] subtree = client
.getSitemapSubtree(GraphQLUtils.convertUriToGraphQLContentNamespace(request.getUriType()),
request.getLocalizationId(), node.getId(), depth, NONE, contextData);
if (node.getItems() == null) {
node.setItems(new ArrayList<>());
}
if (subtree.length > 0 && subtree[0].getItems() != null) {
node.getItems().addAll(subtree[0].getItems());
}
leafNodes.addAll(getLeafNodes(node));
}
}
return Optional.of(taxonomySitemapItem);
}
@Override
public Optional> getNavigationSubtree(@NotNull SitemapRequestDto requestDto) {
try {
List navigation = getNavigationSubtreeInternal(requestDto);
Collection result = convert(navigation);
// this needed because this interface method has a wrong result type
// usually, empty collections means 'NO DATA'
if (result.isEmpty()) {
return Optional.empty();
} else {
return Optional.of(result);
}
} catch (ApiClientException e) {
log.warn("Cannot find/load/convert dynamic subtree navigation in PCA for the request " + requestDto, e);
return Optional.empty();
}
}
private List getNavigationSubtreeInternal(@NotNull SitemapRequestDto requestDto) {
int depth = getDepth(requestDto);
String sitemapId = requestDto.getSitemapId();
List siteMapsItems;
if (requestDto.getExpandLevels().isUnlimited()) {
siteMapsItems = getEntireNavigationSubtreeInternal(requestDto);
} else {
boolean withAncestors = requestDto.getNavigationFilter().isWithAncestors();
if (withAncestors) {
// we are looking for a particular item, we need to request the entire subtree first
siteMapsItems = getEntireNavigationSubtreeInternal(requestDto);
// Prune descendants from our desired node
SitemapItem node = findNode(siteMapsItems, sitemapId);
prune(node, 0, depth);
return siteMapsItems;
} else {
if (sitemapId == null && depth > 0) {
// Requesting from root so just return descendants from root
depth--;
}
siteMapsItems = asList(provider.getClient()
.getSitemapSubtree(GraphQLUtils.convertUriToGraphQLContentNamespace(requestDto.getUriType()),
requestDto.getLocalizationId(), sitemapId, depth, NONE, null));
}
}
if (sitemapId == null) {
// if parent node not specified we return entire tree
return siteMapsItems
.stream()
.sorted(Comparator.comparing(sitemapItem -> sitemapItem.getOriginalTitle()))
.collect(Collectors.toList());
} else {
// root node specified so return the direct children of this node
return siteMapsItems
.stream()
.flatMap(sitemapItem -> ((TaxonomySitemapItem) sitemapItem).getItems().stream())
.sorted(Comparator.comparing(sitemapItem -> sitemapItem.getOriginalTitle()))
.collect(Collectors.toList());
}
}
private List getEntireNavigationSubtreeInternal(@NotNull SitemapRequestDto request) {
int depth = defaultDescendantDepth;
com.sdl.web.pca.client.ApiClient client = provider.getClient();
List rootItems = asList(client.getSitemapSubtree(
GraphQLUtils.convertUriToGraphQLContentNamespace(request.getUriType()),
request.getLocalizationId(),
request.getSitemapId(),
depth,
request.getNavigationFilter().isWithAncestors() ? Ancestor.INCLUDE : NONE,
null));
if (rootItems.isEmpty()) {
return emptyList();
}
List tempRoots = new ArrayList<>(rootItems);
int index = 0;
while (index < tempRoots.size()) {
TaxonomySitemapItem root = (TaxonomySitemapItem) tempRoots.get(index);
List leafNodes = getLeafNodes(root);
for (TaxonomySitemapItem item : leafNodes) {
TaxonomySitemapItem[] subtree = client.getSitemapSubtree(GraphQLUtils
.convertUriToGraphQLContentNamespace(request.getUriType()),
request.getLocalizationId(),
item.getId(),
depth,
NONE,
null);
if (subtree.length > 0) {
item.setItems(subtree[0].getItems());
tempRoots.addAll(getLeafNodes(item));
}
}
index++;
}
return rootItems;
}
private List getLeafNodes(SitemapItem rootNode) {
List leafNodes = new ArrayList<>();
if (!(rootNode instanceof TaxonomySitemapItem)) {
return leafNodes;
}
TaxonomySitemapItem root = (TaxonomySitemapItem) rootNode;
if (!root.getHasChildNodes() || root.getItems() == null || root.getItems().isEmpty()) {
return leafNodes;
}
for (SitemapItem item : root.getItems()) {
if (item instanceof TaxonomySitemapItem) {
TaxonomySitemapItem node = (TaxonomySitemapItem) item;
if (node.getHasChildNodes()) {
if (node.getItems() == null || node.getItems().isEmpty()) {
leafNodes.add(node);
} else {
leafNodes.addAll(getLeafNodes(node));
}
}
}
}
return leafNodes;
}
private SitemapItem findNode(List roots, String sitemapId) {
for (SitemapItem node : roots) {
if (node.getId().equals(sitemapId)) {
return node;
}
if (node instanceof TaxonomySitemapItem) {
TaxonomySitemapItem item = (TaxonomySitemapItem) node;
if (item.getItems() != null && item.getItems().size() > 0) {
SitemapItem result = findNode(item.getItems(), sitemapId);
if (result != null) {
return result;
}
}
}
}
return null;
}
private void prune(SitemapItem root, int currentLevel, int descendantLevels) {
if (!(root instanceof TaxonomySitemapItem)) {
return;
}
TaxonomySitemapItem item = (TaxonomySitemapItem) root;
if (item.getItems() == null || item.getItems().size() == 0) {
return;
}
if (currentLevel < descendantLevels) {
for (SitemapItem next : item.getItems()) {
prune(next, currentLevel + 1, descendantLevels);
}
} else {
if (item.getItems() != null) {
item.getItems().clear();
}
}
}
private int getDepth(@NotNull SitemapRequestDto requestDto) {
int depth;
DepthCounter expandLevels = requestDto.getExpandLevels();
if (expandLevels.isUnlimited()) {
depth = defaultDescendantDepth;
} else {
depth = expandLevels.getCounter();
}
return depth;
}
Collection convert(List source) {
return source.stream().map(sitemapItem -> convert(sitemapItem)).collect(Collectors.toList());
}
SitemapItemModelData convert(SitemapItem source) {
SitemapItemModelData target;
if (source instanceof TaxonomySitemapItem) {
target = new TaxonomyNodeModelData();
} else if (source instanceof PageSitemapItem) {
target = new SitemapItemModelData();
} else {
throw new ApiClientException("Unsupported sitemap type " + source.getClass().getCanonicalName());
}
target.setId(source.getId());
target.setOriginalTitle(source.getOriginalTitle());
target.setTitle(source.getTitle());
target.setType(source.getType());
if (source.getPublishedDate() != null) {
target.setPublishedDate(DateTime.parse(source.getPublishedDate()));
}
target.setUrl(source.getUrl());
target.setVisible(source.getVisible());
if (source instanceof TaxonomySitemapItem) {
TaxonomyNodeModelData targetNode = (TaxonomyNodeModelData) target;
TaxonomySitemapItem sourceNode = (TaxonomySitemapItem) source;
targetNode.setKey(sourceNode.getKey());
targetNode.setClassifiedItemsCount(sourceNode.getClassifiedItemsCount());
targetNode.setDescription(sourceNode.getDescription());
targetNode.setWithChildren(sourceNode.getHasChildNodes());
targetNode.setTaxonomyAbstract(sourceNode.getAbstract());
if (sourceNode.getItems() == null || sourceNode.getItems().isEmpty()) {
return targetNode;
}
for (SitemapItem item : sourceNode.getItems()) {
targetNode.getItems().add(convert(item));
}
}
return target;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy