1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.rest.delivery.jcr.v1;
35
36 import info.magnolia.cms.security.JCRSessionOp;
37 import info.magnolia.cms.util.PathUtil;
38 import info.magnolia.context.Context;
39 import info.magnolia.context.MgnlContext;
40 import info.magnolia.jcr.util.NodeUtil;
41 import info.magnolia.rest.AbstractEndpoint;
42 import info.magnolia.rest.delivery.jcr.NodesResult;
43 import info.magnolia.rest.delivery.jcr.QueryBuilder;
44 import info.magnolia.rest.delivery.jcr.filter.FilteringContentDecoratorBuilder;
45
46 import java.util.Arrays;
47 import java.util.Collections;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.stream.Collectors;
51
52 import javax.inject.Inject;
53 import javax.inject.Provider;
54 import javax.jcr.Node;
55 import javax.jcr.NodeIterator;
56 import javax.jcr.RepositoryException;
57 import javax.jcr.Session;
58 import javax.jcr.Workspace;
59 import javax.jcr.query.Query;
60 import javax.jcr.query.QueryResult;
61 import javax.ws.rs.DefaultValue;
62 import javax.ws.rs.GET;
63 import javax.ws.rs.NotFoundException;
64 import javax.ws.rs.Path;
65 import javax.ws.rs.PathParam;
66 import javax.ws.rs.Produces;
67 import javax.ws.rs.QueryParam;
68 import javax.ws.rs.core.MediaType;
69 import javax.ws.rs.core.UriInfo;
70
71 import org.apache.commons.lang3.StringUtils;
72
73
74
75
76
77
78
79
80
81
82
83
84
85 @Path("/delivery/{endpointPrefix}/v1")
86 public class JcrDeliveryEndpoint extends AbstractEndpoint<JcrDeliveryEndpointDefinition> {
87
88 private static final String ENDPOINT_PARAM = "endpointPrefix";
89
90 private static final String PATH_PARAM = "path";
91 private static final String NODE_TYPES_PARAM = "nodeTypes";
92 private static final String KEYWORD_PARAM = "q";
93 private static final String ORDER_BY_PARAM = "orderBy";
94 private static final String OFFSET_PARAM = "offset";
95 private static final String LIMIT_PARAM = "limit";
96
97 private static final List<String> ENDPOINT_PARAMETERS = Collections.unmodifiableList(Arrays.asList(
98 NODE_TYPES_PARAM,
99 KEYWORD_PARAM,
100 ORDER_BY_PARAM,
101 OFFSET_PARAM,
102 LIMIT_PARAM
103 ));
104
105 private final Map<String, WorkspaceParameters> params;
106 private final Provider<Context> contextProvider;
107
108 @javax.ws.rs.core.Context
109 private UriInfo uriInfo;
110
111 @Inject
112 public JcrDeliveryEndpoint(JcrDeliveryEndpointDefinition endpointDefinition, Provider<Context> contextProvider) {
113 super(endpointDefinition);
114 this.contextProvider = contextProvider;
115 this.params = getEndpointDefinition().getParams();
116 }
117
118
119
120
121 @GET
122 @Path("/{path:.*}")
123 @Produces({ MediaType.APPLICATION_JSON })
124 public Node readNode(
125 @PathParam(ENDPOINT_PARAM) String endpointPrefix,
126 @PathParam(PATH_PARAM) @DefaultValue("/") String path) throws RepositoryException {
127
128 WorkspaceParameters param = params.get(endpointPrefix);
129
130 if (param != null) {
131 String workspace = param.getWorkspace();
132 if (param.getWorkspace() == null) {
133
134 workspace = endpointPrefix;
135 }
136
137 return doSessionOperation(workspace, param.isBypassWorkspaceAcls(), new JCRSessionOp<Node>(workspace) {
138 @Override
139 public Node exec(Session session) throws RepositoryException {
140 String nodePath = PathUtil.createPath(param.getRootPath(), path);
141 Node node = session.getNode(nodePath);
142
143 if (param.getNodeTypes() != null && !param.getNodeTypes().isEmpty()) {
144 boolean matchingNodeType = false;
145 for (String nodeType : param.getNodeTypes()) {
146 if (NodeUtil.isNodeType(node, nodeType)) {
147 matchingNodeType = true;
148 break;
149 }
150 }
151 if (!matchingNodeType) {
152 throw new NotFoundException(String.format("Node '%s' does not match any configured node type", node));
153 }
154 }
155
156 FilteringContentDecoratorBuilder decorators = new FilteringContentDecoratorBuilder()
157 .childNodeTypes(param.getChildNodeTypes())
158 .depth(param.getDepth())
159 .includeSystemProperties(param.isIncludeSystemProperties())
160 .references(param.getReferences());
161
162 node = decorators.wrapNode(node);
163
164 return node;
165 }
166 });
167 }
168
169 throw new NotFoundException(String.format("No workspace-params entry for endpoint prefix '%s'", endpointPrefix));
170 }
171
172
173
174
175 @GET
176 @Produces({ MediaType.APPLICATION_JSON })
177 public NodesResult queryNodes(@PathParam(ENDPOINT_PARAM) String endpointPrefix,
178 @QueryParam(KEYWORD_PARAM) String keyword,
179 @QueryParam(ORDER_BY_PARAM) String orderByParam,
180 @QueryParam(OFFSET_PARAM) Long offsetParam,
181 @QueryParam(LIMIT_PARAM) Long limitParam) throws RepositoryException {
182
183 WorkspaceParameters param = params.get(endpointPrefix);
184
185 if (param != null) {
186 String workspace = param.getWorkspace();
187 if (param.getWorkspace() == null) {
188
189 workspace = endpointPrefix;
190 }
191
192 long offset = offsetParam == null ? 0 : offsetParam;
193 long limit = limitParam == null ? param.getLimit() : limitParam;
194 List<String> propertiesToOrderBy = StringUtils.isEmpty(orderByParam) ? Arrays.asList("@name asc") : Arrays.asList(StringUtils.split(orderByParam, ","));
195 Map<String, List<String>> filteringConditions = uriInfo.getQueryParameters().entrySet().stream()
196 .filter(entry -> !ENDPOINT_PARAMETERS.contains(entry.getKey()) && !entry.getValue().isEmpty())
197 .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue()));
198
199 NodeIterator results = doSessionOperation(workspace, param.isBypassWorkspaceAcls(), new JCRSessionOp<NodeIterator>(workspace) {
200 @Override
201 public NodeIterator exec(Session session) throws RepositoryException {
202 Workspace workspaceObj = session.getWorkspace();
203
204 Query query = QueryBuilder.inWorkspace(workspaceObj)
205 .rootPath(param.getRootPath())
206 .nodeTypes(param.getNodeTypes())
207 .keyword(keyword)
208 .conditions(filteringConditions)
209 .orderBy(propertiesToOrderBy)
210 .offset(offset)
211 .limit(limit)
212 .build();
213
214 QueryResult result = query.execute();
215 NodeIterator nodeIterator = result.getNodes();
216
217 FilteringContentDecoratorBuilder decorators = new FilteringContentDecoratorBuilder()
218 .childNodeTypes(param.getChildNodeTypes())
219 .depth(param.getDepth())
220 .includeSystemProperties(param.isIncludeSystemProperties())
221 .references(param.getReferences());
222
223 nodeIterator = decorators.wrapNodeIterator(nodeIterator);
224
225 return nodeIterator;
226 }
227 });
228
229 return new NodesResult(results, 0);
230 }
231
232 throw new NotFoundException(String.format("No workspace-params entry for endpoint prefix '%s'", endpointPrefix));
233 }
234
235 protected <R> R doSessionOperation(String workspace, boolean bypassWorkspaceAcls, JCRSessionOp<R> operation) throws RepositoryException {
236 if (bypassWorkspaceAcls) {
237 return MgnlContext.doInSystemContext(operation);
238 }
239
240 return operation.exec(contextProvider.get().getJCRSession(workspace));
241 }
242 }