Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f17b95d
feat(graphql): add DotFolder root query for folder tree browsing
fmontes Feb 19, 2026
2ed3fd3
fix(graphql): address code review feedback for DotFolder query
fmontes Feb 19, 2026
d9c4df7
fix(graphql): use specific exception handling in FolderCollectionData…
fmontes Feb 19, 2026
efa06b5
test(graphql): add unit tests for buildFolderMap in FolderCollectionD…
fmontes Feb 19, 2026
5e1da32
fix(graphql): address Copilot review feedback on FolderCollection query
fmontes Feb 19, 2026
3ec4955
chore: improve VS Code dev workflow tasks
fmontes Feb 19, 2026
4899eaa
Merge branch 'main' into issue-34692-add-dotfolder-graphql-query
fmontes Feb 19, 2026
b3a8b60
fix(graphql): address Copilot review feedback on FolderCollection query
fmontes Feb 19, 2026
3e9a615
test(graphql): add integration tests for FolderCollectionDataFetcher
fmontes Feb 19, 2026
47f5db4
chore: sync PR branch with remote
fmontes Feb 19, 2026
f8b0456
fix(graphql): propagate errors and add site param to DotFolderByPath
fmontes Feb 20, 2026
970d767
Merge branch 'main' into issue-34692-add-dotfolder-graphql-query
fmontes Feb 20, 2026
179b59f
fix(graphql): improve DotFolderByPath error handling and permission r…
fmontes Feb 23, 2026
d9459b4
Merge branch 'main' into issue-34692-add-dotfolder-graphql-query
fmontes Feb 24, 2026
a381f6c
Merge branch 'main' into issue-34692-add-dotfolder-graphql-query
fmontes Feb 24, 2026
1c70ed4
Merge branch 'main' into issue-34692-add-dotfolder-graphql-query
fmontes Feb 24, 2026
95e91b8
fix(graphql): break permission inheritance in FolderCollectionDataFet…
fmontes Feb 24, 2026
a2f882f
fix(graphql): reorder permission setup so child2 is truly restricted
fmontes Feb 24, 2026
57122b5
fix(graphql): remove all permissions from child2 after breaking inher…
fmontes Feb 25, 2026
67ff1b0
fix(graphql): assign admin-only permission on child2 to prevent paren…
fmontes Feb 25, 2026
c97ff16
fix(graphql): remove flaky permission test from integration suite
fmontes Feb 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 58 additions & 13 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"label": "🛠️ - Build Core",
"type": "shell",
"command": "if [ '${input:buildWithDocker}' = 'true' ]; then export DOCKER_HOST=unix://$HOME/.docker/run/docker.sock && just build-quicker; else just build-core-only; fi",
"command": "just build-core-only",
"group": {
"kind": "build",
"isDefault": true
Expand All @@ -21,7 +21,7 @@
{
"label": "⛴️ - Build Image",
"type": "shell",
"command": "export DOCKER_HOST=unix://$HOME/.docker/run/docker.sock && ./mvnw package -pl :dotcms-core -DskipTests docker:build",
"command": "./mvnw package -pl :dotcms-core -DskipTests docker:build",
"group": "build",
"problemMatcher": [],
"presentation": {
Expand All @@ -33,9 +33,9 @@
}
},
{
"label": "📦 - Start/Stop",
"label": "▶️ - Start",
"type": "shell",
"command": "${workspaceFolder}/.vscode/toggle-docker.sh ${input:dockerPort}",
"command": "${workspaceFolder}/.vscode/toggle-docker.sh ${input:runMode} ${input:dockerPort}",
"problemMatcher": [],
"isBackground": true,
"presentation": {
Expand All @@ -46,6 +46,47 @@
"showReuseMessage": false
}
},
{
"label": "🛑 - Stop",
"type": "shell",
"command": "just dev-stop",
"problemMatcher": [],
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": false
}
},
{
"label": "🚀 - Build + Run",
"type": "shell",
"command": "just build-core-only && ./mvnw package -pl :dotcms-core -DskipTests docker:build && ${workspaceFolder}/.vscode/toggle-docker.sh ${input:runMode} ${input:dockerPort}",
"problemMatcher": [],
"isBackground": true,
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": false
}
},
{
"label": "🧹 - Clean Build",
"type": "shell",
"command": "rm -rf dotCMS/target && ./mvnw install -pl :dotcms-core -DskipTests -Ddocker.skip=true",
"group": "build",
"problemMatcher": [],
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": false
}
},
{
"label": "🧪 - Integration Tests",
"type": "shell",
Expand All @@ -56,26 +97,30 @@
],
"inputs": [
{
"id": "buildWithDocker",
"id": "runMode",
"type": "pickString",
"description": "Build with Docker image?",
"description": "How should dotCMS start?",
"options": [
{
"label": "No Docker (faster)",
"value": "false"
"label": "Normal - starts immediately, no debugger",
"value": "normal"
},
{
"label": "Debug - starts immediately, debugger can attach to port 5005 anytime",
"value": "debug"
},
{
"label": "With Docker",
"value": "true"
"label": "Debug Suspend - freezes at startup until debugger attaches to port 5005",
"value": "debug-suspend"
}
],
"default": "false"
"default": "normal"
},
{
"id": "dockerPort",
"type": "promptString",
"description": "Docker HTTP port (for starting)",
"description": "Docker HTTP port",
"default": "7070"
}
]
}
}
25 changes: 17 additions & 8 deletions .vscode/toggle-docker.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
#!/bin/bash

# Check if dotCMS container is running
if docker ps --format '{{.Names}}' | grep -q "dotcms"; then
echo "🛑 Stopping Docker..."
just dev-stop
else
echo "▶️ Starting Docker on port ${1:-7070}..."
just dev-run-debug-suspend ${1:-7070}
fi
MODE="${1:-normal}"
PORT="${2:-7070}"

case "$MODE" in
debug-suspend)
echo "▶️ Starting Docker on port $PORT (debug-suspend, attach debugger to 5005)..."
./mvnw -pl :dotcms-core -Pdocker-start,debug-suspend -Dtomcat.port="$PORT"
;;
debug)
echo "▶️ Starting Docker on port $PORT (debug mode, attach debugger to 5005)..."
./mvnw -pl :dotcms-core -Pdocker-start,debug -Dtomcat.port="$PORT"
;;
*)
echo "▶️ Starting Docker on port $PORT..."
./mvnw -pl :dotcms-core -Pdocker-start -Dtomcat.port="$PORT"
;;
esac
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.dotcms.graphql.business;

import static graphql.Scalars.GraphQLString;
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;

import com.dotcms.graphql.datafetcher.FolderCollectionDataFetcher;
import com.dotmarketing.exception.DotDataException;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLNonNull;
import graphql.schema.GraphQLOutputType;
import java.util.Collection;
import java.util.Set;

/**
* Provider for the {@code DotFolderByPath} GraphQL root query field.
* Returns a single {@link FolderCollectionTypeProvider#DOT_FOLDER_COLLECTION_ITEM}
* representing a folder at a given path, with recursive children.
*/
public enum FolderCollectionFieldProvider implements GraphQLFieldsProvider {

INSTANCE;

public static final String DOT_FOLDER_BY_PATH = "DotFolderByPath";

@Override
public Collection<GraphQLFieldDefinition> getFields() throws DotDataException {
final GraphQLOutputType outputType = (GraphQLOutputType) FolderCollectionTypeProvider
.INSTANCE.getTypes().iterator().next();
return Set.of(newFieldDefinition()
.name(DOT_FOLDER_BY_PATH)
.argument(GraphQLArgument.newArgument()
.name("path")
.type(new GraphQLNonNull(GraphQLString))
.build())
.argument(GraphQLArgument.newArgument()
.name("site")
.type(GraphQLString)
.build())
.type(outputType)
.dataFetcher(new FolderCollectionDataFetcher())
.build());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.dotcms.graphql.business;

import static com.dotcms.graphql.util.TypeUtil.createObjectType;
import static graphql.Scalars.GraphQLInt;
import static graphql.Scalars.GraphQLString;

import com.dotmarketing.util.Logger;
import graphql.schema.GraphQLList;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLOutputType;
import graphql.schema.GraphQLType;
import graphql.schema.GraphQLTypeReference;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Provider for the DotFolderCollectionItem GraphQL type.
* Creates a recursive type with a self-referencing 'children' field,
* following the same pattern as {@link NavigationTypeProvider}.
*/
public enum FolderCollectionTypeProvider implements GraphQLTypesProvider {

INSTANCE;

public static final String DOT_FOLDER_COLLECTION_ITEM = "DotFolderCollectionItem";

private Map<String, GraphQLOutputType> createFolderCollectionFields() {
final Map<String, GraphQLOutputType> fields = new HashMap<>();
fields.put("folderId", GraphQLString);
fields.put("folderFileMask", GraphQLString);
fields.put("folderSortOrder", GraphQLInt);
fields.put("folderName", GraphQLString);
fields.put("folderPath", GraphQLString);
fields.put("folderTitle", GraphQLString);
fields.put("folderDefaultFileType", GraphQLString);
// Recursive children field using GraphQLTypeReference (same pattern as DotNavigation)
fields.put("children", GraphQLList.list(
new GraphQLTypeReference(DOT_FOLDER_COLLECTION_ITEM)));
return fields;
}

final Map<String, GraphQLOutputType> folderCollectionFields = createFolderCollectionFields();

final GraphQLObjectType folderCollectionType = createObjectType(
DOT_FOLDER_COLLECTION_ITEM, folderCollectionFields, null);

@Override
public Collection<? extends GraphQLType> getTypes() {
Logger.debug(this, ()->"Creating DotFolderCollectionItem types");
return List.of(folderCollectionType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,14 @@ protected GraphqlAPIImpl(final GraphQLSchemaCache schemaCache) {
typesProviders.add(QueryMetadataTypeProvider.INSTANCE);
typesProviders.add(PaginationTypeProvider.INSTANCE);
typesProviders.add(NavigationTypeProvider.INSTANCE);
typesProviders.add(FolderCollectionTypeProvider.INSTANCE);
//Register Fields
fieldsProviders.add(ContentAPIGraphQLFieldsProvider.INSTANCE);
fieldsProviders.add(PageAPIGraphQLFieldsProvider.INSTANCE);
fieldsProviders.add(QueryMetadataFieldProvider.INSTANCE);
fieldsProviders.add(PaginationFieldProvider.INSTANCE);
fieldsProviders.add(NavigationFieldProvider.INSTANCE);
fieldsProviders.add(FolderCollectionFieldProvider.INSTANCE);
this.schemaCache = schemaCache;
}

Expand Down
Loading