From 19e3959b59d66e70a62597e7cc8da31e09e9006c Mon Sep 17 00:00:00 2001 From: "Serhii Plyhun (commercial)" Date: Wed, 6 Nov 2024 14:00:10 +0100 Subject: [PATCH] Sorting over binary metadata / micronode content (#1599) * SUP-16258: Sorting removes permission checks * UT. Fixes. * Tests. More fixes. * NULL order checks ignore * Unneeded test * Changelog * Non content sorting fix * Adapt use name param * SQL injection prevention (ugly) * Correct check for SQL injected sort param * Sanity fix * Fix paging of sorted data * Total count * Nicronode/binary/noderef sorting fixes * Match the filter/sort fields with type provider values * More sorting tests * Micronode/binary sort * More missed permission checks * More occurrences of manual count fetch * Fix paging fetch * Add missing test * No demo in Jenkinsfiles * Update changelog items * More UTs * Fix for the node field sorting * Fix incorrect permission application * Permission regression * Prepare for next feature version * Added request cancelation into interface; Fixed interface naming; Fixed publishing scripts * Corrected Mesh-JS Versions * Regression fix * Update * Regression fixes --------- Co-authored-by: Norbert Pomaroli Co-authored-by: Decker Dominik --- bom/pom.xml | 2 +- .../entries/2024/04/7757.GPU-1413.enhancement | 1 + .../gentics/mesh/core/data/MeshVertex.java | 16 +++++ .../data/relationship/GraphRelationship.java | 43 +++++++++++- .../data/relationship/GraphRelationships.java | 26 +++++++- .../mesh/graphdb/MeshOrientGraphQuery.java | 57 ++++++++++++---- .../impl/AbstractGraphFieldContainerImpl.java | 27 ++++++++ .../impl/NodeGraphFieldContainerImpl.java | 21 +----- .../core/data/generic/MeshVertexImpl.java | 51 +++++++++++---- .../mesh/core/data/impl/TagFamilyImpl.java | 2 +- .../core/data/node/impl/MicronodeImpl.java | 6 ++ .../mesh/core/data/node/impl/NodeImpl.java | 2 +- .../data/root/impl/AbstractRootVertex.java | 2 +- .../core/data/root/impl/NodeRootImpl.java | 5 ++ .../mesh/assertj/impl/JsonObjectAssert.java | 17 +++++ .../core/graphql/GraphQLEndpointTest.java | 33 +++++++++- .../core/language/LanguageEndpointTest.java | 8 ++- .../filtering/nodes-micronode-field-native | 2 +- .../nodes-micronodelist-field-native | 4 +- .../filtering/nodes-nodereferences-java | 4 +- .../filtering/nodes-nodereferences-native | 4 +- .../filtering/nodes-s3binary-field-native | 2 +- .../graphql/filtering/nodes-sorted-binary | 40 ++++++++++++ .../graphql/filtering/nodes-sorted-binary.v1 | 40 ++++++++++++ .../graphql/filtering/nodes-sorted-micronode | 48 ++++++++++++++ .../filtering/nodes-sorted-micronode.v1 | 46 +++++++++++++ .../graphql/filtering/nodes-sorted-node-field | 65 +++++++++++++++++++ .../filtering/nodes-sorted-node-field.v1 | 65 +++++++++++++++++++ .../graphql/filter/BinaryFieldFilter.java | 4 +- .../mesh/graphql/filter/BinaryFilter.java | 5 +- .../graphql/filter/EntityReferenceFilter.java | 7 +- .../mesh/graphql/filter/ImageDataFilter.java | 1 - .../mesh/graphql/filter/S3BinaryFilter.java | 6 +- 33 files changed, 581 insertions(+), 81 deletions(-) create mode 100644 changelog/src/changelog/entries/2024/04/7757.GPU-1413.enhancement create mode 100644 tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-binary create mode 100644 tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-binary.v1 create mode 100644 tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-micronode create mode 100644 tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-micronode.v1 create mode 100644 tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-node-field create mode 100644 tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-node-field.v1 diff --git a/bom/pom.xml b/bom/pom.xml index b26e103a0a..1c840a2c47 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -21,7 +21,7 @@ 21.5 21.0 3.2.2 - 3.0.5 + 3.0.6 3.11.0 3.3.1 5.5.7 diff --git a/changelog/src/changelog/entries/2024/04/7757.GPU-1413.enhancement b/changelog/src/changelog/entries/2024/04/7757.GPU-1413.enhancement new file mode 100644 index 0000000000..5d6d14d9e5 --- /dev/null +++ b/changelog/src/changelog/entries/2024/04/7757.GPU-1413.enhancement @@ -0,0 +1 @@ +Core: Now it is possible to sort over binary metadata, node reference and micronode non-list fields. \ No newline at end of file diff --git a/mdm/orientdb-api/src/main/java/com/gentics/mesh/core/data/MeshVertex.java b/mdm/orientdb-api/src/main/java/com/gentics/mesh/core/data/MeshVertex.java index 4d8ec2004f..ee268a7306 100644 --- a/mdm/orientdb-api/src/main/java/com/gentics/mesh/core/data/MeshVertex.java +++ b/mdm/orientdb-api/src/main/java/com/gentics/mesh/core/data/MeshVertex.java @@ -8,11 +8,13 @@ import com.gentics.graphqlfilter.filter.operation.FilterOperation; import com.gentics.mesh.context.BulkActionContext; import com.gentics.mesh.context.impl.DummyBulkActionContext; +import com.gentics.mesh.core.data.dao.PersistingRootDao; import com.gentics.mesh.core.data.perm.InternalPermission; import com.gentics.mesh.core.data.user.HibUser; import com.gentics.mesh.core.rest.common.ContainerType; import com.gentics.mesh.graphdb.model.MeshElement; import com.gentics.mesh.madl.frame.VertexFrame; +import com.gentics.mesh.parameter.PagingParameters; import com.tinkerpop.blueprints.Vertex; /** @@ -96,4 +98,18 @@ default void delete() { default String parseFilter(FilterOperation filter, ContainerType ctype, HibUser user, InternalPermission permission, Optional maybeOwner) { return parseFilter(filter, ctype) + permissionFilter(user, permission, maybeOwner, Optional.ofNullable(ctype)).map(permFilter -> " AND " + permFilter).orElse(StringUtils.EMPTY); } + + /** + * Set up native permission filter if the sorting is requested, since the sorting forces the native SQL data fetcher. + * + * @param pagingInfo + * @param user + * @param permission + * @param maybeOwner + * @param containerType + * @return + */ + default Optional permissionFilterIfRequired(PagingParameters pagingInfo, HibUser user, InternalPermission permission, Optional maybeOwner, Optional containerType) { + return PersistingRootDao.shouldSort(pagingInfo) ? permissionFilter(user, permission, maybeOwner, containerType) : Optional.empty(); + } } diff --git a/mdm/orientdb-api/src/main/java/com/gentics/mesh/core/data/relationship/GraphRelationship.java b/mdm/orientdb-api/src/main/java/com/gentics/mesh/core/data/relationship/GraphRelationship.java index 4b5c9d1841..d4b63c8e00 100644 --- a/mdm/orientdb-api/src/main/java/com/gentics/mesh/core/data/relationship/GraphRelationship.java +++ b/mdm/orientdb-api/src/main/java/com/gentics/mesh/core/data/relationship/GraphRelationship.java @@ -1,5 +1,7 @@ package com.gentics.mesh.core.data.relationship; +import java.util.Optional; + /** * A class for determining the relationship of a vertex class over the edge labels and edge properties * @@ -12,6 +14,8 @@ public class GraphRelationship { private final Class relatedVertexClass; private final String edgeFieldName; private final String defaultEdgeFieldFilterValue; + private final Optional maybeEdgeLevelFields; + private final boolean skipMapping; /** * Constructor @@ -20,13 +24,43 @@ public class GraphRelationship { * @param relatedVertexClass a vertex class name, where the edge goes to * @param edgeFieldName name of an edge property, that is being used in the filtering. If null/empty, no edge property filtering used * @param defaultEdgeFieldFilterValue default value for the edge property filtering. Unused, if edgeFieldName is null/empty. + * @param maybeEdgeLevelFields optional list of entity fields, that are applied on an input edge level. */ public GraphRelationship(String edgeName, Class relatedVertexClass, String edgeFieldName, - String defaultEdgeFieldFilterValue) { + String defaultEdgeFieldFilterValue, Optional maybeEdgeLevelFields) { + this(edgeName, relatedVertexClass, edgeFieldName, defaultEdgeFieldFilterValue, maybeEdgeLevelFields, false); + } + + /** + * Constructor + * + * @param edgeName an edge label + * @param relatedVertexClass a vertex class name, where the edge goes to + * @param edgeFieldName name of an edge property, that is being used in the filtering. If null/empty, no edge property filtering used + * @param defaultEdgeFieldFilterValue default value for the edge property filtering. Unused, if edgeFieldName is null/empty. + * @param maybeEdgeLevelFields optional list of entity fields, that are applied on an input edge level. + * @param skipMapping this mapping is artificial and should not be used in either filtering or sorting. + */ + public GraphRelationship(String edgeName, Class relatedVertexClass, String edgeFieldName, + String defaultEdgeFieldFilterValue, Optional maybeEdgeLevelFields, boolean skipMapping) { this.edgeName = edgeName; this.relatedVertexClass = relatedVertexClass; this.edgeFieldName = edgeFieldName; this.defaultEdgeFieldFilterValue = defaultEdgeFieldFilterValue; + this.maybeEdgeLevelFields = maybeEdgeLevelFields; + this.skipMapping = skipMapping; + } + /** + * Constructor with optional edge fields. + * + * @param edgeName an edge label + * @param relatedVertexClass a vertex class name, where the edge goes to + * @param edgeFieldName name of an edge property, that is being used in the filtering. If null/empty, no edge property filtering used + * @param defaultEdgeFieldFilterValue default value for the edge property filtering. Unused, if edgeFieldName is null/empty. + */ + public GraphRelationship(String edgeName, Class relatedVertexClass, String edgeFieldName, + String defaultEdgeFieldFilterValue) { + this(edgeName, relatedVertexClass, edgeFieldName, defaultEdgeFieldFilterValue, Optional.empty()); } public String getEdgeName() { return edgeName; @@ -40,4 +74,11 @@ public String getEdgeFieldName() { public String getDefaultEdgeFieldFilterValue() { return defaultEdgeFieldFilterValue; } + public Optional getMaybeEdgeLevelFields() { + return maybeEdgeLevelFields; + } + + public boolean isSkipMapping() { + return skipMapping; + } } diff --git a/mdm/orientdb-api/src/main/java/com/gentics/mesh/core/data/relationship/GraphRelationships.java b/mdm/orientdb-api/src/main/java/com/gentics/mesh/core/data/relationship/GraphRelationships.java index 0354b943e6..3b9a6440c8 100644 --- a/mdm/orientdb-api/src/main/java/com/gentics/mesh/core/data/relationship/GraphRelationships.java +++ b/mdm/orientdb-api/src/main/java/com/gentics/mesh/core/data/relationship/GraphRelationships.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Optional; import com.gentics.madl.index.IndexHandler; import com.gentics.madl.type.TypeHandler; @@ -19,11 +20,32 @@ public class GraphRelationships { * Add a relation between entities through an edge. */ public static void addRelation(Class keyClass, Class valueClass, String mappingName, String relationName, String edgeFieldName, String defaultEdgeFieldFilterValue) { + addRelation(keyClass, valueClass, mappingName, relationName, edgeFieldName, defaultEdgeFieldFilterValue, Optional.empty(), false); + } + + /** + * Add a relation between entities through an edge, with optional edge fields. + */ + public static void addRelation(Class keyClass, Class valueClass, String mappingName, String relationName, String edgeFieldName, String defaultEdgeFieldFilterValue, Optional maybeEdgeLevelFieldNames, boolean skipMapping) { Map relations = VERTEX_RELATIONS.getOrDefault(keyClass, new HashMap<>()); - relations.put(mappingName, new GraphRelationship(relationName, valueClass, edgeFieldName, defaultEdgeFieldFilterValue)); + relations.put(mappingName, new GraphRelationship(relationName, valueClass, edgeFieldName, defaultEdgeFieldFilterValue, maybeEdgeLevelFieldNames, skipMapping)); VERTEX_RELATIONS.put(keyClass, relations); } + /** + * Add a relation between entities through UUID. + * + * @param + * @param + * @param keyClass + * @param valueClass + * @param mappingName + * @param relationName + */ + public static void addUnmappedRelation(Class keyClass, Class valueClass, String mappingName) { + addRelation(keyClass, valueClass, mappingName, MeshVertex.UUID_KEY, mappingName, null, Optional.empty(), true); + } + /** * Add a relation between entities through UUID. * @@ -35,7 +57,7 @@ public static void addRelation(Clas * @param relationName */ public static void addRelation(Class keyClass, Class valueClass, String mappingName) { - addRelation(keyClass, valueClass, mappingName, MeshVertex.UUID_KEY, mappingName, null); + addRelation(keyClass, valueClass, mappingName, MeshVertex.UUID_KEY, mappingName, null, Optional.empty(), false); } /** diff --git a/mdm/orientdb-api/src/main/java/com/gentics/mesh/graphdb/MeshOrientGraphQuery.java b/mdm/orientdb-api/src/main/java/com/gentics/mesh/graphdb/MeshOrientGraphQuery.java index f34e71a421..8fb65245c1 100644 --- a/mdm/orientdb-api/src/main/java/com/gentics/mesh/graphdb/MeshOrientGraphQuery.java +++ b/mdm/orientdb-api/src/main/java/com/gentics/mesh/graphdb/MeshOrientGraphQuery.java @@ -1,5 +1,6 @@ package com.gentics.mesh.graphdb; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; @@ -204,12 +205,24 @@ protected void buildOrderFieldRequest(StringBuilder text, boolean isEdgeRequest, String sanitizedPart = sanitizeInput(sortParts[0]); String[] pathParts = sanitizedPart.split("\\."); text.append(", "); - Map relation = GraphRelationships.findRelation(currentMapping); + Map relation = null; + Optional maybeEdgeLevelFields = Optional.empty(); for (int i = 0; i < pathParts.length; i++) { String pathPart = pathParts[i]; + relation = GraphRelationships.findRelation(currentMapping); + Map localRelation = relation; + Optional maybeFieldTypeMapping = Optional.ofNullable(localRelation) + .flatMap(relation1 -> relation1.keySet().stream() + .filter(key -> key.startsWith("*") && pathPart.matches("[\\S]" + key)) + .findAny()); if (relation != null && !sanitizedPart.endsWith(pathPart) - && (relation.containsKey(pathPart) || (relation.containsKey("*")))) { - GraphRelationship relationMapping = relation.get(pathPart) != null ? relation.get(pathPart) : relation.get("*"); + && (relation.containsKey(pathPart) || (relation.containsKey("*") || maybeFieldTypeMapping.isPresent()))) { + GraphRelationship relationMapping = maybeFieldTypeMapping.map(key -> localRelation.get(key)) + .or(() -> Optional.ofNullable(localRelation.get(pathPart))).orElseGet(() -> localRelation.get("*")); + if (relationMapping.isSkipMapping()) { + continue; + } + maybeEdgeLevelFields = relationMapping.getMaybeEdgeLevelFields(); if (useEdgeFilters && relationMapping != null) { if (MeshVertex.UUID_KEY.equals(relationMapping.getEdgeName())) { String partName = pathParts.length > 1 ? pathParts[1] : pathPart; @@ -230,6 +243,10 @@ protected void buildOrderFieldRequest(StringBuilder text, boolean isEdgeRequest, escapeFieldNameIfRequired(text, relationMapping.getEdgeFieldName()); text.append(")"); } else { + String localPathPart = pathPart; + if (maybeFieldTypeMapping.isPresent()) { + localPathPart = pathPart.split("-")[0]; + } if (i < 1 && isEdgeRequest) { text.append(relationDirection.name().toLowerCase()); text.append("V()."); @@ -240,10 +257,12 @@ protected void buildOrderFieldRequest(StringBuilder text, boolean isEdgeRequest, text.append("')["); text.append(relationMapping.getEdgeFieldName()); text.append("='"); - text.append(relation.get(pathPart) != null ? relationMapping.getDefaultEdgeFieldFilterValue() : pathPart); + text.append(localRelation.get(localPathPart) != null ? relationMapping.getDefaultEdgeFieldFilterValue() : localPathPart); text.append("']."); - text.append(vertexLookupDirOpposite.name().toLowerCase()); - text.append("V()"); + if (maybeEdgeLevelFields.isEmpty()) { + text.append(vertexLookupDirOpposite.name().toLowerCase()); + text.append("V()"); + } } } else { if (i < 1 && isEdgeRequest) { @@ -255,19 +274,31 @@ protected void buildOrderFieldRequest(StringBuilder text, boolean isEdgeRequest, escapeFieldNameIfRequired(text, relationMapping.getEdgeName()); text.append(")"); } - text.append("[0]."); - currentMapping = relationMapping.getRelatedVertexClass(); - } else if (i == 1 && "fields".equals(pathParts[0]) && pathParts.length > 2) { - // skip the schema name + if (maybeEdgeLevelFields.isEmpty()) { + text.append("[0]."); + currentMapping = relationMapping.getRelatedVertexClass(); + } + } else if ((i > 0 && "fields".equals(pathParts[i-1])) || (i < (pathParts.length-1) && "fields".equals(pathParts[i+1])) && pathParts.length > 2) { + // skip the schema name or the self-reference continue; } else { if (i < 1 && isEdgeRequest) { text.append(relationDirection.name().toLowerCase()); text.append("V()."); } - text.append("`"); - text.append(pathPart); - text.append("`"); + maybeEdgeLevelFields + .flatMap(edgeLevelFields -> Arrays.stream(edgeLevelFields).filter(field -> field.equals(pathPart)).findAny()) + .ifPresentOrElse(field -> { + text.append("`"); + text.append(field); + text.append("`"); + text.append("[0]"); + }, () -> { + text.append("`"); + text.append(pathPart); + text.append("`"); + }); + maybeEdgeLevelFields = Optional.empty(); } } text.append(" as "); diff --git a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/container/impl/AbstractGraphFieldContainerImpl.java b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/container/impl/AbstractGraphFieldContainerImpl.java index b6cfcd721f..8b3679a848 100644 --- a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/container/impl/AbstractGraphFieldContainerImpl.java +++ b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/container/impl/AbstractGraphFieldContainerImpl.java @@ -9,9 +9,12 @@ import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; import java.util.List; +import java.util.Optional; import org.apache.commons.lang3.StringUtils; +import com.gentics.madl.index.IndexHandler; +import com.gentics.madl.type.TypeHandler; import com.gentics.mesh.context.BulkActionContext; import com.gentics.mesh.context.InternalActionContext; import com.gentics.mesh.core.data.GraphFieldContainer; @@ -20,6 +23,7 @@ import com.gentics.mesh.core.data.binary.HibBinary; import com.gentics.mesh.core.data.binary.HibImageVariant; import com.gentics.mesh.core.data.binary.impl.BinaryGraphFieldVariantImpl; +import com.gentics.mesh.core.data.binary.impl.BinaryImpl; import com.gentics.mesh.core.data.impl.GraphFieldTypes; import com.gentics.mesh.core.data.node.HibNode; import com.gentics.mesh.core.data.node.Micronode; @@ -60,7 +64,10 @@ import com.gentics.mesh.core.data.node.field.nesting.MicronodeGraphField; import com.gentics.mesh.core.data.node.field.nesting.NodeGraphField; import com.gentics.mesh.core.data.node.impl.MicronodeImpl; +import com.gentics.mesh.core.data.node.impl.NodeImpl; +import com.gentics.mesh.core.data.relationship.GraphRelationships; import com.gentics.mesh.core.data.s3binary.S3HibBinary; +import com.gentics.mesh.core.data.s3binary.impl.S3BinaryImpl; import com.gentics.mesh.core.data.schema.HibMicroschemaVersion; import com.gentics.mesh.core.rest.error.GenericRestException; import com.gentics.mesh.core.rest.node.FieldMap; @@ -78,6 +85,26 @@ public abstract class AbstractGraphFieldContainerImpl extends AbstractBasicGraphFieldContainerImpl implements GraphFieldContainer { static final Logger log = LoggerFactory.getLogger(AbstractGraphFieldContainerImpl.class); + /** + * Initialize the vertex type and indices. + * + * @param type + * @param index + */ + public static void init(TypeHandler type, IndexHandler index, Class cls) { + GraphRelationships.addRelation(cls, MicronodeImpl.class, "*-micronode", HAS_FIELD, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); + GraphRelationships.addRelation(cls, NodeImpl.class, "*-node", HAS_FIELD, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); + GraphRelationships.addRelation(cls, BinaryImpl.class, "*-binary", HAS_FIELD, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY, + Optional.of(new String[] { BinaryGraphField.BINARY_IMAGE_DOMINANT_COLOR_PROPERTY_KEY, BinaryGraphField.BINARY_CONTENT_TYPE_PROPERTY_KEY, BinaryGraphField.BINARY_FILENAME_PROPERTY_KEY }), false); + GraphRelationships.addRelation(cls, S3BinaryImpl.class, "*-s3binary", HAS_FIELD, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); + GraphRelationships.addRelation(cls, StringGraphFieldListImpl.class, "*-stringlist", HAS_LIST, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); + GraphRelationships.addRelation(cls, NumberGraphFieldListImpl.class, "*-numberlist", HAS_LIST, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); + GraphRelationships.addRelation(cls, BooleanGraphFieldListImpl.class, "*-booleanlist", HAS_LIST, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); + GraphRelationships.addRelation(cls, HtmlGraphFieldListImpl.class, "*-htmllist", HAS_LIST, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); + GraphRelationships.addRelation(cls, NodeGraphFieldListImpl.class, "*-nodelist", HAS_LIST, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); + GraphRelationships.addRelation(cls, MicronodeGraphFieldListImpl.class, "*-micronodelist", HAS_LIST, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); + } + /** * Return the parent node of the field container. * diff --git a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/container/impl/NodeGraphFieldContainerImpl.java b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/container/impl/NodeGraphFieldContainerImpl.java index b3d14e3b63..5268733f17 100644 --- a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/container/impl/NodeGraphFieldContainerImpl.java +++ b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/container/impl/NodeGraphFieldContainerImpl.java @@ -26,8 +26,6 @@ import java.util.Set; import java.util.stream.Collectors; -import org.apache.commons.lang3.StringUtils; - import com.gentics.madl.index.IndexHandler; import com.gentics.madl.type.TypeHandler; import com.gentics.mesh.context.BulkActionContext; @@ -37,31 +35,22 @@ import com.gentics.mesh.core.data.HibNodeFieldContainer; import com.gentics.mesh.core.data.HibNodeFieldContainerEdge; import com.gentics.mesh.core.data.NodeGraphFieldContainer; -import com.gentics.mesh.core.data.binary.impl.BinaryImpl; import com.gentics.mesh.core.data.branch.HibBranch; import com.gentics.mesh.core.data.dao.ContentDao; import com.gentics.mesh.core.data.generic.MeshVertexImpl; import com.gentics.mesh.core.data.impl.GraphFieldContainerEdgeImpl; import com.gentics.mesh.core.data.node.field.BinaryGraphField; -import com.gentics.mesh.core.data.node.field.GraphField; import com.gentics.mesh.core.data.node.field.S3BinaryGraphField; import com.gentics.mesh.core.data.node.field.StringGraphField; import com.gentics.mesh.core.data.node.field.impl.BinaryGraphFieldImpl; import com.gentics.mesh.core.data.node.field.impl.MicronodeGraphFieldImpl; import com.gentics.mesh.core.data.node.field.impl.S3BinaryGraphFieldImpl; import com.gentics.mesh.core.data.node.field.list.HibMicronodeFieldList; -import com.gentics.mesh.core.data.node.field.list.impl.BooleanGraphFieldListImpl; -import com.gentics.mesh.core.data.node.field.list.impl.HtmlGraphFieldListImpl; import com.gentics.mesh.core.data.node.field.list.impl.MicronodeGraphFieldListImpl; -import com.gentics.mesh.core.data.node.field.list.impl.NodeGraphFieldListImpl; -import com.gentics.mesh.core.data.node.field.list.impl.NumberGraphFieldListImpl; -import com.gentics.mesh.core.data.node.field.list.impl.StringGraphFieldListImpl; import com.gentics.mesh.core.data.node.field.nesting.HibMicronodeField; import com.gentics.mesh.core.data.node.field.nesting.MicronodeGraphField; -import com.gentics.mesh.core.data.node.impl.MicronodeImpl; import com.gentics.mesh.core.data.node.impl.NodeImpl; import com.gentics.mesh.core.data.relationship.GraphRelationships; -import com.gentics.mesh.core.data.s3binary.impl.S3BinaryImpl; import com.gentics.mesh.core.data.schema.HibFieldSchemaVersionElement; import com.gentics.mesh.core.data.schema.HibSchemaVersion; import com.gentics.mesh.core.data.schema.impl.SchemaContainerVersionImpl; @@ -113,15 +102,7 @@ public static void init(TypeHandler type, IndexHandler index) { .withType(NOTUNIQUE) .withPostfix("bucket")); - GraphRelationships.addRelation(NodeGraphFieldContainerImpl.class, MicronodeImpl.class, "*", HAS_FIELD, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); - GraphRelationships.addRelation(NodeGraphFieldContainerImpl.class, BinaryImpl.class, "*", HAS_FIELD, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); - GraphRelationships.addRelation(NodeGraphFieldContainerImpl.class, S3BinaryImpl.class, "*", HAS_FIELD, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); - GraphRelationships.addRelation(NodeGraphFieldContainerImpl.class, StringGraphFieldListImpl.class, "*", HAS_LIST, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); - GraphRelationships.addRelation(NodeGraphFieldContainerImpl.class, NumberGraphFieldListImpl.class, "*", HAS_LIST, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); - GraphRelationships.addRelation(NodeGraphFieldContainerImpl.class, BooleanGraphFieldListImpl.class, "*", HAS_LIST, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); - GraphRelationships.addRelation(NodeGraphFieldContainerImpl.class, HtmlGraphFieldListImpl.class, "*", HAS_LIST, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); - GraphRelationships.addRelation(NodeGraphFieldContainerImpl.class, NodeGraphFieldListImpl.class, "*", HAS_LIST, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); - GraphRelationships.addRelation(NodeGraphFieldContainerImpl.class, MicronodeGraphFieldListImpl.class, "*", HAS_LIST, GraphField.FIELD_KEY_PROPERTY_KEY, StringUtils.EMPTY); + AbstractGraphFieldContainerImpl.init(type, index, NodeGraphFieldContainerImpl.class); } @Override diff --git a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/generic/MeshVertexImpl.java b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/generic/MeshVertexImpl.java index f696e49630..8a6dc1b2fc 100644 --- a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/generic/MeshVertexImpl.java +++ b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/generic/MeshVertexImpl.java @@ -55,6 +55,7 @@ import com.gentics.mesh.core.data.relationship.GraphRelationship; import com.gentics.mesh.core.data.relationship.GraphRelationships; import com.gentics.mesh.core.data.role.HibRole; +import com.gentics.mesh.core.data.schema.HibMicroschema; import com.gentics.mesh.core.data.schema.HibSchema; import com.gentics.mesh.core.data.schema.impl.SchemaContainerImpl; import com.gentics.mesh.core.data.schema.impl.SchemaContainerVersionImpl; @@ -260,25 +261,47 @@ protected S mapSorting(S sorting) { sorting.clearSort(); items.entrySet().stream().forEach(entry -> { String key = mapGraphQlFieldNameForSorting(entry.getKey()); - String[] keyParts = key.split("\\."); - if (keyParts.length > 1) { - // We expect format of 'fields.'. - if ("fields".equals(keyParts[0])) { - if (keyParts.length > 2) { - HibSchema schema = Tx.get().schemaDao().findByName(keyParts[1]); - if (schema == null) { - throw new IllegalArgumentException("No schema found for sorting request: " + key); + String[] fieldParts = key.split("fields\\."); + String newKey = StringUtils.EMPTY; + if (fieldParts.length > 1) { + for (int i = 0 ; i < fieldParts.length; i++) { + String part = fieldParts[i]; + String[] keyParts = part.split("\\."); + if (keyParts.length > 1) { + HibSchema schema = Tx.get().schemaDao().findByName(keyParts[0]); + String type; + if (schema != null) { + FieldSchema field = schema.getLatestVersion().getSchema().getFieldsAsMap().get(keyParts[1]); + if (field == null) { + throw new IllegalArgumentException("No field found for sorting request: " + key); + } + type = field.getType().toLowerCase(); + } else { + HibMicroschema microschema = Tx.get().microschemaDao().findByName(keyParts[0]); + if (microschema != null) { + // + FieldSchema field = microschema.getLatestVersion().getSchema().getFieldsAsMap().get(keyParts[1]); + if (field == null) { + throw new IllegalArgumentException("No field found for sorting request: " + key); + } + type = field.getType().toLowerCase(); + } else { + throw new IllegalArgumentException("No schema found for sorting request: " + key); + } + } + if (i > 0 && newKey.length() > 0) { + newKey += "."; } - FieldSchema field = schema.getLatestVersion().getSchema().getFieldsAsMap().get(keyParts[2]); - if (field == null) { - throw new IllegalArgumentException("No field found for sorting request: " + key); + newKey += "fields." + keyParts[0] + "." + keyParts[1] + "-" + type; + if (keyParts.length > 2) { + newKey += Arrays.stream(keyParts).skip(2).map(this::mapGraphQlFieldNameForSorting).collect(Collectors.joining(".", ".", StringUtils.EMPTY)); } - key = keyParts[0] + "." + keyParts[2] + "-" + field.getType().toLowerCase(); } - // else process as non-schema content field } + } else { + newKey = key; } - sorting.putSort(key, entry.getValue()); + sorting.putSort(newKey, entry.getValue()); }); return sorting; } diff --git a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/impl/TagFamilyImpl.java b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/impl/TagFamilyImpl.java index ad16aa6f25..87a32ab528 100644 --- a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/impl/TagFamilyImpl.java +++ b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/impl/TagFamilyImpl.java @@ -233,7 +233,7 @@ public Page findAll(InternalActionContext ac, PagingParameters pa PagingParameters sorting = mapSorting(pagingInfo); Optional maybeParsedFilter = maybeExtraFilter .map(extraFilter -> parseFilter(extraFilter, ContainerType.PUBLISHED, ac.getUser(), READ_PERM, Optional.empty())) - .or(() -> permissionFilter(ac.getUser(), READ_PERM, Optional.empty(), Optional.empty())); + .or(() -> permissionFilterIfRequired(pagingInfo, ac.getUser(), READ_PERM, Optional.empty(), Optional.empty())); Stream stream = toStream(db().getVertices( getPersistanceClass(), new String[] {}, diff --git a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/node/impl/MicronodeImpl.java b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/node/impl/MicronodeImpl.java index 0144653fd2..1918bf8900 100644 --- a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/node/impl/MicronodeImpl.java +++ b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/node/impl/MicronodeImpl.java @@ -20,6 +20,7 @@ import com.gentics.mesh.core.data.node.HibNode; import com.gentics.mesh.core.data.node.Micronode; import com.gentics.mesh.core.data.node.field.list.HibMicronodeFieldList; +import com.gentics.mesh.core.data.relationship.GraphRelationships; import com.gentics.mesh.core.data.schema.HibFieldSchemaVersionElement; import com.gentics.mesh.core.data.schema.HibMicroschemaVersion; import com.gentics.mesh.core.rest.common.FieldTypes; @@ -54,6 +55,11 @@ public static void init(TypeHandler type, IndexHandler index) { .withField(MICROSCHEMA_VERSION_KEY_PROPERTY, FieldType.STRING)); index.createIndex(VertexIndexDefinition.vertexIndex(MicronodeImpl.class) .withField(MICROSCHEMA_VERSION_KEY_PROPERTY, FieldType.STRING)); + + GraphRelationships.addUnmappedRelation(MicronodeImpl.class, MicronodeImpl.class, "micronode"); + GraphRelationships.addUnmappedRelation(MicronodeImpl.class, MicronodeImpl.class, "fields"); + + AbstractGraphFieldContainerImpl.init(type, index, MicronodeImpl.class); } @Override diff --git a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/node/impl/NodeImpl.java b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/node/impl/NodeImpl.java index 73afb9940c..7388bc3b8e 100644 --- a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/node/impl/NodeImpl.java +++ b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/node/impl/NodeImpl.java @@ -139,6 +139,7 @@ public static void init(TypeHandler type, IndexHandler index) { .withPostfix("branch_parents") .withField(BRANCH_PARENTS_KEY_PROPERTY, STRING_SET)); + GraphRelationships.addUnmappedRelation(NodeImpl.class, NodeImpl.class, "node"); GraphRelationships.addRelation(NodeImpl.class, NodeGraphFieldContainerImpl.class, "fields", HAS_FIELD_CONTAINER, "edgeType", ContainerType.INITIAL.getCode()); GraphRelationships.addRelation(NodeImpl.class, SchemaContainerImpl.class, "schema"); GraphRelationships.addRelation(NodeImpl.class, UserImpl.class, "creator"); @@ -246,7 +247,6 @@ public Result getChildren() { new Object[] { getUuid() }), NodeImpl.class)); } - @Override public Result getChildren(String branchUuid, ContainerType containerType, PagingParameters sorting, Optional> maybeFilter, Optional maybeUser) { InternalPermission perm = containerType == PUBLISHED ? READ_PUBLISHED_PERM : READ_PERM; Tx tx = GraphDBTx.getGraphTx(); diff --git a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/root/impl/AbstractRootVertex.java b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/root/impl/AbstractRootVertex.java index 47f45b5b24..860974e97c 100644 --- a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/root/impl/AbstractRootVertex.java +++ b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/root/impl/AbstractRootVertex.java @@ -192,7 +192,7 @@ protected Page findAll(InternalActionContext ac, PagingParameters p PagingParameters sorting = mapSorting(pagingInfo); Optional maybeParsedFilter = maybeExtraFilter .map(extraFilter -> parseFilter(extraFilter, ctype, ac.getUser(), perm, Optional.empty())) - .or(() -> permissionFilter(ac.getUser(), perm, Optional.empty(), Optional.of(ctype))); + .or(() -> permissionFilterIfRequired(pagingInfo, ac.getUser(), perm, Optional.empty(), Optional.of(ctype))); Stream stream = toStream(db().getVertices( getPersistanceClass(), new String[] {}, diff --git a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/root/impl/NodeRootImpl.java b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/root/impl/NodeRootImpl.java index 6f2e8744c7..71ded46cfb 100644 --- a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/root/impl/NodeRootImpl.java +++ b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/data/root/impl/NodeRootImpl.java @@ -32,6 +32,7 @@ import com.gentics.mesh.core.data.impl.GraphFieldContainerEdgeImpl; import com.gentics.mesh.core.data.impl.ProjectImpl; import com.gentics.mesh.core.data.node.Node; +import com.gentics.mesh.core.data.node.field.BinaryGraphField; import com.gentics.mesh.core.data.node.impl.NodeImpl; import com.gentics.mesh.core.data.page.Page; import com.gentics.mesh.core.data.page.impl.DynamicTransformableStreamPageImpl; @@ -146,6 +147,7 @@ private Stream findAll(HibUser user, InternalPermission perm, String pro maybeContainerType, maybeFilter .map(f -> parseFilter(f, maybeContainerType.orElse(PUBLISHED), user, perm, Optional.empty())) + .or(() -> permissionFilterIfRequired(paging, user, perm, Optional.empty(), maybeContainerType)) )); } @@ -208,6 +210,9 @@ public String mapGraphQlFieldNameForSorting(String gqlName) { switch (gqlName) { case "edited": return "fields.last_edited_timestamp"; case "editor": return "fields.editor"; + case "fileName": return BinaryGraphField.BINARY_FILENAME_PROPERTY_KEY; + case "imageDominantColor": return BinaryGraphField.BINARY_IMAGE_DOMINANT_COLOR_PROPERTY_KEY; + case "mimeType": return BinaryGraphField.BINARY_CONTENT_TYPE_PROPERTY_KEY; } return super.mapGraphQlFieldNameForSorting(gqlName); } diff --git a/tests/common/src/main/java/com/gentics/mesh/assertj/impl/JsonObjectAssert.java b/tests/common/src/main/java/com/gentics/mesh/assertj/impl/JsonObjectAssert.java index 84a31efd63..e335de744b 100644 --- a/tests/common/src/main/java/com/gentics/mesh/assertj/impl/JsonObjectAssert.java +++ b/tests/common/src/main/java/com/gentics/mesh/assertj/impl/JsonObjectAssert.java @@ -226,6 +226,8 @@ public JsonObjectAssert compliesTo(String assertion, String msg, Integer lineNr) pathIsDate(path, msg); } else if ("".equals(value)) { pathIsUndefined(path, msg); + } else if ("".equals(value)) { + pathIsEmpty(path, msg); } else { has(path, replaceVariables(value), msg); } @@ -262,6 +264,21 @@ public JsonObjectAssert pathIsUndefined(String path, String msg) { return this; } + @SuppressWarnings("rawtypes") + private JsonObjectAssert pathIsEmpty(String path, String msg) { + try { + Object value = JsonPath.read(actual.toString(), path); + if (value instanceof Map && ((Map) value).size() > 0) { + fail(msg + " The value at path {" + path + "} is not empty, while should be."); + } else if (value instanceof Collection && ((Collection) value).size() > 0) { + fail(msg + " The value at path {" + path + "} is not empty, while should be."); + } + } catch (PathNotFoundException e) { + fail(msg + " The value at path {" + path + "} was not present but it should be present but empty."); + } + return this; + } + /** * Assert that the string value which is present at the given path matches the pattern of a uuid. * diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/graphql/GraphQLEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/graphql/GraphQLEndpointTest.java index ebd8327365..9302af8d81 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/graphql/GraphQLEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/graphql/GraphQLEndpointTest.java @@ -212,7 +212,10 @@ protected static Stream> queries() { Arrays.asList("filtering/nodes-binary-field-native", true, false, "draft"), Arrays.asList("filtering/nodes-s3binary-field-native", true, false, "draft"), Arrays.asList("filtering/nodes-nodelist-field-native", true, false, "draft"), - Arrays.asList("filtering/nodes-micronodelist-field-native", true, false, "draft") + Arrays.asList("filtering/nodes-micronodelist-field-native", true, false, "draft"), + Arrays.asList("filtering/nodes-sorted-micronode", true, false, "draft"), + Arrays.asList("filtering/nodes-sorted-binary", true, false, "draft"), + Arrays.asList("filtering/nodes-sorted-node-field", true, false, "draft") ); } @@ -412,7 +415,7 @@ public void testNodeQuery() throws Exception { HibBinary binary = tx.binaries().create("hashsumvalue", 1L).runInExistingTx(tx); binary.setImageHeight(10).setImageWidth(20).setSize(2048); container.createBinary("binary", binary).setImageDominantColor("00FF00") - .setImageFocalPoint(new FocalPoint(0.2f, 0.3f)).setMimeType("image/jpeg"); + .setImageFocalPoint(new FocalPoint(0.2f, 0.3f)).setMimeType("image/jpeg").setFileName("some_image.jpg"); // s3binary S3HibBinary s3binary = tx.s3binaries().create(UUIDUtil.randomUUID(), node.getUuid() + "/s3", "test.jpg").runInExistingTx(tx); @@ -477,6 +480,30 @@ public void testNodeQuery() throws Exception { micronodeField.getMicronode().createString("lastName").setString("Doe"); micronodeField.getMicronode().createString("address").setString("Somewhere"); micronodeField.getMicronode().createString("postcode").setString("1010"); + + // Second micronode + HibNodeFieldContainer container3 = tx.contentDao().createFieldContainer(node3, "en", initialBranch(), user()); + // micronodeList + micronodeList = container3.createMicronodeList("micronodeList"); + firstMicronode = micronodeList.createMicronode(microschemaContainer("vcard").getLatestVersion()); + firstMicronode.createString("firstName").setString("Jane"); + firstMicronode.createString("lastName").setString("Dow"); + firstMicronode.createString("address").setString("Overthere"); + firstMicronode.createString("postcode").setString("8010"); + + secondMicronode = micronodeList.createMicronode(microschemaDao.findByUuid(microschemaUuid).getLatestVersion()); + secondMicronode.createString("text").setString("Jane"); + secondMicronode.createNode("nodeRef", content()); + micrnodeNodeList = secondMicronode.createNodeList("nodeList"); + micrnodeNodeList.createNode(0, node2); + micrnodeNodeList.createNode(1, node3); + + // micronode + micronodeField = container3.createMicronode("micronode", microschemaContainer("vcard").getLatestVersion()); + micronodeField.getMicronode().createString("firstName").setString("Jane"); + micronodeField.getMicronode().createString("lastName").setString("Dow"); + micronodeField.getMicronode().createString("address").setString("Overthere"); + micronodeField.getMicronode().createString("postcode").setString("8010"); } tx.contentDao().updateWebrootPathInfo(container, initialBranchUuid(), null); tx.success(); @@ -539,7 +566,7 @@ public void testNodeQuery() throws Exception { assertion.accept(jsonResponse); } } catch (Throwable e) { - getTestContext().LOG.error("Assertion failed for: \n{}\nPayload:\n{}", query, response.toJson()); + getTestContext().LOG.error("Assertion failed for: \n{}\nPayload:\n{}", query, jsonResponse.encodePrettily()); throw e; } } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/language/LanguageEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/language/LanguageEndpointTest.java index 249985c681..8b73564a9a 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/language/LanguageEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/language/LanguageEndpointTest.java @@ -16,6 +16,7 @@ import com.gentics.mesh.FieldUtil; import com.gentics.mesh.core.data.perm.InternalPermission; +import com.gentics.mesh.core.rest.SortOrder; import com.gentics.mesh.core.rest.error.GenericRestException; import com.gentics.mesh.core.rest.lang.LanguageListResponse; import com.gentics.mesh.core.rest.lang.LanguageResponse; @@ -26,6 +27,7 @@ import com.gentics.mesh.parameter.client.GenericParametersImpl; import com.gentics.mesh.parameter.impl.NodeParametersImpl; import com.gentics.mesh.parameter.impl.ProjectLoadParametersImpl; +import com.gentics.mesh.parameter.impl.SortingParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.TestSize; import com.gentics.mesh.test.context.AbstractMeshTest; @@ -369,9 +371,11 @@ public void testNotPermittedProject() { } } - @Override @Test - @Ignore + @Override public void testReadPermittedSorted() throws Exception { + LanguageListResponse list = call(() -> client().findLanguages(new SortingParametersImpl("name", SortOrder.DESCENDING))); + assertEquals("Total data size should be 11", 11, list.getData().size()); + assertThat(list.getData()).isSortedAccordingTo((a, b) -> b.getName().compareTo(a.getName())); } } diff --git a/tests/tests-core/src/main/resources/graphql/filtering/nodes-micronode-field-native b/tests/tests-core/src/main/resources/graphql/filtering/nodes-micronode-field-native index 0054e31ea0..58dc9db4d5 100644 --- a/tests/tests-core/src/main/resources/graphql/filtering/nodes-micronode-field-native +++ b/tests/tests-core/src/main/resources/graphql/filtering/nodes-micronode-field-native @@ -24,7 +24,7 @@ } } }) { - # [$.data.micronodeIsNull.elements.length()=8] + # [$.data.micronodeIsNull.elements.length()=7] elements { uuid schema { diff --git a/tests/tests-core/src/main/resources/graphql/filtering/nodes-micronodelist-field-native b/tests/tests-core/src/main/resources/graphql/filtering/nodes-micronodelist-field-native index 52dd8496ab..3af939abf1 100644 --- a/tests/tests-core/src/main/resources/graphql/filtering/nodes-micronodelist-field-native +++ b/tests/tests-core/src/main/resources/graphql/filtering/nodes-micronodelist-field-native @@ -66,7 +66,7 @@ } } }) { - # [$.data.noneMatch.elements.length()=0] + # [$.data.noneMatch.elements.length()=1] elements { uuid schema { @@ -91,7 +91,7 @@ } } }) { - # [$.data.anyNotMatch.elements.length()=1] + # [$.data.anyNotMatch.elements.length()=2] elements { uuid schema { diff --git a/tests/tests-core/src/main/resources/graphql/filtering/nodes-nodereferences-java b/tests/tests-core/src/main/resources/graphql/filtering/nodes-nodereferences-java index 2ceb2bf77d..94870c9fc1 100644 --- a/tests/tests-core/src/main/resources/graphql/filtering/nodes-nodereferences-java +++ b/tests/tests-core/src/main/resources/graphql/filtering/nodes-nodereferences-java @@ -83,7 +83,7 @@ count: { gt: 1 } } }) { - # [$.data.hasMicronodeReferences.elements.length()=1] + # [$.data.hasMicronodeReferences.elements.length()=2] elements { uuid schema { @@ -96,7 +96,7 @@ count: { gt: 2 } } }) { - # [$.data.moreThan2References.elements.length()=2] + # [$.data.moreThan2References.elements.length()=3] elements { uuid schema { diff --git a/tests/tests-core/src/main/resources/graphql/filtering/nodes-nodereferences-native b/tests/tests-core/src/main/resources/graphql/filtering/nodes-nodereferences-native index 9406069125..557f5dd349 100644 --- a/tests/tests-core/src/main/resources/graphql/filtering/nodes-nodereferences-native +++ b/tests/tests-core/src/main/resources/graphql/filtering/nodes-nodereferences-native @@ -97,7 +97,7 @@ count: { gt: 1 } } }) { - # [$.data.hasMicronodeReferences.elements.length()=1] + # [$.data.hasMicronodeReferences.elements.length()=2] elements { uuid schema { @@ -110,7 +110,7 @@ count: { gt: 2 } } }) { - # [$.data.moreThan2References.elements.length()=2] + # [$.data.moreThan2References.elements.length()=3] elements { uuid schema { diff --git a/tests/tests-core/src/main/resources/graphql/filtering/nodes-s3binary-field-native b/tests/tests-core/src/main/resources/graphql/filtering/nodes-s3binary-field-native index 700fba56cc..42e931b7fe 100644 --- a/tests/tests-core/src/main/resources/graphql/filtering/nodes-s3binary-field-native +++ b/tests/tests-core/src/main/resources/graphql/filtering/nodes-s3binary-field-native @@ -20,7 +20,7 @@ schema: {is: folder} fields: { folder: { - s3Binary: {s3binary:{key: { like: "%/s3" }}} + s3Binary: {s3binary:{s3ObjectKey: { like: "%/s3" }}} } } }) { diff --git a/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-binary b/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-binary new file mode 100644 index 0000000000..a0ee48c5da --- /dev/null +++ b/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-binary @@ -0,0 +1,40 @@ +{ + nodes(filter: { + schema: {is: folder} + } + sort: { + fields: { + folder: { + binary: { + fileName: DESCENDING + } + } + } + } + ) { + # [$.data.nodes.elements.length()=9] + elements { + uuid + schema { + name + } + ... on folder { + fields { + binary { + # [$.data.nodes.elements[0].fields.binary=] + # [$.data.nodes.elements[1].fields.binary=] + # [$.data.nodes.elements[2].fields.binary=] + # [$.data.nodes.elements[3].fields.binary=] + # [$.data.nodes.elements[4].fields.binary=] + # [$.data.nodes.elements[5].fields.binary=] + # [$.data.nodes.elements[6].fields.binary=] + # [$.data.nodes.elements[7].fields.binary=] + # [$.data.nodes.elements[8].fields.binary.fileName=some_image.jpg] + fileName + } + } + } + } + } +} +# [$.errors=] \ No newline at end of file diff --git a/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-binary.v1 b/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-binary.v1 new file mode 100644 index 0000000000..b35de08493 --- /dev/null +++ b/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-binary.v1 @@ -0,0 +1,40 @@ +{ + nodes(filter: { + schema: {is: folder} + } + sort: { + fields: { + folder: { + binary: { + fileName: DESCENDING + } + } + } + } + ) { + # [$.data.nodes.elements.length()=9] + elements { + uuid + schema { + name + } + fields { + ... on folder { + binary { + # [$.data.nodes.elements[0].fields.binary=] + # [$.data.nodes.elements[1].fields.binary=] + # [$.data.nodes.elements[2].fields.binary=] + # [$.data.nodes.elements[3].fields.binary=] + # [$.data.nodes.elements[4].fields.binary=] + # [$.data.nodes.elements[5].fields.binary=] + # [$.data.nodes.elements[6].fields.binary=] + # [$.data.nodes.elements[7].fields.binary=] + # [$.data.nodes.elements[8].fields.binary.fileName=some_image.jpg] + fileName + } + } + } + } + } +} +# [$.errors=] \ No newline at end of file diff --git a/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-micronode b/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-micronode new file mode 100644 index 0000000000..fcace1b664 --- /dev/null +++ b/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-micronode @@ -0,0 +1,48 @@ +{ + nodes(filter: { + schema: {is: folder} + fields: { + folder: { + micronode: {micronode: {microschema: {is: vcard} }} + } + } + } + sort: { + fields: { + folder: { + micronode: { + micronode: { + fields: { + vcard: { + address: DESCENDING + } + } + } + } + } + } + } + ) { + # [$.data.nodes.elements.length()=2] + # [$.data.nodes.elements[0].fields.micronode.fields.address=Somewhere] + # [$.data.nodes.elements[1].fields.micronode.fields.address=Overthere] + elements { + uuid + schema { + name + } + ... on folder { + fields { + micronode { + ... on vcard { + fields { + address + } + } + } + } + } + } + } +} +# [$.errors=] \ No newline at end of file diff --git a/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-micronode.v1 b/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-micronode.v1 new file mode 100644 index 0000000000..9a9d5c3b0d --- /dev/null +++ b/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-micronode.v1 @@ -0,0 +1,46 @@ +{ + nodes(filter: { + schema: {is: folder} + fields: { + folder: { + micronode: {micronode: {microschema: {is: vcard} }} + } + } + } + sort: { + fields: { + folder: { + micronode: { + micronode: { + fields: { + vcard: { + address: DESCENDING + } + } + } + } + } + } + } + ) { + # [$.data.nodes.elements.length()=2] + # [$.data.nodes.elements[0].fields.micronode.address=Somewhere] + # [$.data.nodes.elements[1].fields.micronode.address=Overthere] + elements { + uuid + schema { + name + } + fields { + ... on folder { + micronode { + ... on vcard { + address + } + } + } + } + } + } +} +# [$.errors=] \ No newline at end of file diff --git a/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-node-field b/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-node-field new file mode 100644 index 0000000000..ece5d3139a --- /dev/null +++ b/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-node-field @@ -0,0 +1,65 @@ +{ + nodeRefSlugLike: nodes( + filter: { + schema: {is: folder} + fields: { + folder: { + nodeRef: {node: {schema: {is: content} fields: { content: { slug: { like: "%.html" } } }}} + } + } + } + sort: { + fields: { + folder: { + nodeRef: {node: { fields: { content: { slug: ASCENDING } }}} + } + } + } + ) { + # [$.data.nodeRefSlugLike.elements.length()=2] + elements { + uuid + schema { + name + } + } + } + + nodeRefIsNotNull: nodes( + filter: { + schema: {is: folder} + fields: { + folder: { + nodeRef: {isNull:false} + } + } + } + sort: { + fields: { + folder: { + nodeRef: {node: { fields: { content: { slug: ASCENDING } }}} + } + } + } + ) { + # [$.data.nodeRefIsNotNull.elements.length()=3] + # [$.data.nodeRefIsNotNull.elements[0].fields.nodeRef=] + # [$.data.nodeRefIsNotNull.elements[1].fields.nodeRef.fields.slug=News Overview.en.html] + # [$.data.nodeRefIsNotNull.elements[2].fields.nodeRef.fields.slug=News Overview.en.html] + elements { + uuid + ... on folder { + fields { + nodeRef { + ... on content { + fields { + slug + } + } + } + } + } + } + } +} +# [$.errors=] \ No newline at end of file diff --git a/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-node-field.v1 b/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-node-field.v1 new file mode 100644 index 0000000000..5bcc0a60c1 --- /dev/null +++ b/tests/tests-core/src/main/resources/graphql/filtering/nodes-sorted-node-field.v1 @@ -0,0 +1,65 @@ +{ + nodeRefSlugLike: nodes( + filter: { + schema: {is: folder} + fields: { + folder: { + nodeRef: {node: {schema: {is: content} fields: { content: { slug: { like: "%.html" } } }}} + } + } + } + sort: { + fields: { + folder: { + nodeRef: {node: { fields: { content: { slug: ASCENDING } }}} + } + } + } + ) { + # [$.data.nodeRefSlugLike.elements.length()=2] + elements { + uuid + schema { + name + } + } + } + + nodeRefIsNotNull: nodes( + filter: { + schema: {is: folder} + fields: { + folder: { + nodeRef: {isNull:false} + } + } + } + sort: { + fields: { + folder: { + nodeRef: {node: { fields: { content: { slug: ASCENDING } }}} + } + } + } + ) { + # [$.data.nodeRefIsNotNull.elements.length()=3] + # [$.data.nodeRefIsNotNull.elements[0].fields.nodeRef.fields=] + # [$.data.nodeRefIsNotNull.elements[1].fields.nodeRef.fields.slug=News Overview.en.html] + # [$.data.nodeRefIsNotNull.elements[2].fields.nodeRef.fields.slug=News Overview.en.html] + elements { + uuid + fields { + ... on folder { + nodeRef { + fields { + ... on content { + slug + } + } + } + } + } + } + } +} +# [$.errors=] \ No newline at end of file diff --git a/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/BinaryFieldFilter.java b/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/BinaryFieldFilter.java index 165a656b28..bb51a9a99b 100644 --- a/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/BinaryFieldFilter.java +++ b/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/BinaryFieldFilter.java @@ -44,8 +44,8 @@ private BinaryFieldFilter(String owner, GraphQLContext context) { @Override protected List> getFilters() { List> filters = super.getFilters(); - filters.add(makeWrappedFieldFilter("filename", "Filters by filename", StringFilter.filter(), HibImageDataField::getFileName)); - filters.add(makeWrappedFieldFilter("mime", "Filters by MIME type", StringFilter.filter(), HibImageDataField::getMimeType)); + filters.add(makeWrappedFieldFilter("fileName", "Filters by filename", StringFilter.filter(), HibImageDataField::getFileName)); + filters.add(makeWrappedFieldFilter("mimeType", "Filters by MIME type", StringFilter.filter(), HibImageDataField::getMimeType)); filters.add(new MappedFilter<>(owner, "variants", "Filters by image variants", ListFilter.imageVariantListFilter(context, "BINARYFIELD"), content -> content == null ? null : (Collection) CommonTx.get().imageVariantDao().getVariants(content, context).list(), Pair.pair("variants", new JoinPart("IMAGEVARIANT", "uuid")))); diff --git a/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/BinaryFilter.java b/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/BinaryFilter.java index e12a799887..f706f646a0 100644 --- a/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/BinaryFilter.java +++ b/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/BinaryFilter.java @@ -43,11 +43,8 @@ private BinaryFilter(String name, String description, GraphQLContext context) { @Override public List> getFilters() { List> filters = super.getFilters(); - filters.add(new MappedFilter<>(owner, "checksum", "Filters by SHA512 checksum", StringFilter.filter(), + filters.add(new MappedFilter<>(owner, "sha512sum", "Filters by SHA512 checksum", StringFilter.filter(), content -> content == null ? null : content.getSHA512Sum())); -// filters.add(new MappedFilter<>(owner, "variants", "Filters by image variants", -// ListFilter.imageVariantListFilter(context, "BINARY"), -// content -> content == null ? null : (Collection) CommonTx.get().binaryDao().getVariants(content, context).list(), Pair.pair("variants", new JoinPart("IMAGEVARIANT", "value")))); return filters; } } diff --git a/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/EntityReferenceFilter.java b/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/EntityReferenceFilter.java index 3387c78f16..cb56782e96 100644 --- a/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/EntityReferenceFilter.java +++ b/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/EntityReferenceFilter.java @@ -28,7 +28,6 @@ import com.gentics.mesh.core.data.node.field.nesting.HibMicronodeField; import com.gentics.mesh.core.data.node.field.nesting.HibNodeField; import com.gentics.mesh.core.data.node.field.nesting.HibReferenceField; -import com.gentics.mesh.core.db.CommonTx; import com.gentics.mesh.core.db.Tx; import com.gentics.mesh.core.rest.common.ContainerType; import com.gentics.mesh.graphql.context.GraphQLContext; @@ -62,17 +61,17 @@ public EntityReferenceFilter(String name, String description, String entityType, List> filters = new ArrayList<>(); filters.add(FilterField.create("isNull", "Tests if the value is null", GraphQLBoolean, query -> value -> query == (value == null), - Optional.of((query) -> wrapReferencingEdgeFilter("isNull", query, FilterField.isNull() )), false, getOwner())); + Optional.of((query) -> wrapReferencingEdgeFilter("isNull", query, FilterField.isNull() )), false, false, getOwner(), Optional.empty())); filters.add(FilterField.create(referenceType, "Checks if any list item does not match the given predicate", referenceFilter.getType(), query -> val -> val != null && referenceFilter.createPredicate(query).test(val.getReferencedEntity()), - Optional.of((query) -> wrapReferencedEntity(query)), true)); + Optional.of((query) -> wrapReferencedEntity(query)), true, referenceFilter.isSortable(), Optional.empty(), Optional.of(referenceFilter.getSortingType()))); return filters; } protected final FilterField makeWrappedFieldFilter(String filterName, String description, Filter edgeTypeFilter, Function mapper) { return FilterField.create(filterName, description, edgeTypeFilter.getType(), query -> val -> val != null && edgeTypeFilter.createPredicate(query).test(mapper.apply(val)), - Optional.of((query) -> wrapReferencingEdgeFilter(filterName, query, edgeTypeFilter)), true, getOwner()); + Optional.of((query) -> wrapReferencingEdgeFilter(filterName, query, edgeTypeFilter)), true, edgeTypeFilter.isSortable(), getOwner(), Optional.of(edgeTypeFilter.getSortingType())); } protected final FilterOperation wrapReferencingEdgeFilter(String fieldName, FilterQuery query, Filter edgeFilter) { diff --git a/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/ImageDataFilter.java b/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/ImageDataFilter.java index 90f9a1b961..151778dd21 100644 --- a/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/ImageDataFilter.java +++ b/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/ImageDataFilter.java @@ -42,7 +42,6 @@ protected ImageDataFilter(String name, String description, String owner) { @Override public boolean isSortable() { - // TODO implement later return false; } } diff --git a/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/S3BinaryFilter.java b/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/S3BinaryFilter.java index dae1089dde..6d7ac07ca5 100644 --- a/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/S3BinaryFilter.java +++ b/verticles/graphql/src/main/java/com/gentics/mesh/graphql/filter/S3BinaryFilter.java @@ -39,9 +39,9 @@ private S3BinaryFilter(String name, String description) { @Override public List> getFilters() { List> filters = super.getFilters(); - filters.add(new MappedFilter<>(owner, "key", "Filters by S3 object key", StringFilter.filter(), content -> content == null ? null : content.getS3ObjectKey())); - filters.add(new MappedFilter<>(owner, "filename", "Filters by S3 object filename", StringFilter.filter(), content -> content == null ? null : content.getFileName())); - filters.add(new MappedFilter<>(owner, "mime", "Filters by S3 object MIME type", StringFilter.filter(), content -> content == null ? null : content.getMimeType())); + filters.add(new MappedFilter<>(owner, "s3ObjectKey", "Filters by S3 object key", StringFilter.filter(), content -> content == null ? null : content.getS3ObjectKey())); + filters.add(new MappedFilter<>(owner, "fileName", "Filters by S3 object filename", StringFilter.filter(), content -> content == null ? null : content.getFileName())); + filters.add(new MappedFilter<>(owner, "mimeType", "Filters by S3 object MIME type", StringFilter.filter(), content -> content == null ? null : content.getMimeType())); return filters; } } \ No newline at end of file