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
86 @Path("/delivery/{endpointPrefix}/v1")
87 public class JcrDeliveryEndpoint extends AbstractEndpoint<JcrDeliveryEndpointDefinition> {
88
89 private static final String ENDPOINT_PARAM = "endpointPrefix";
90
91 private static final String PATH_PARAM = "path";
92 private static final String NODE_TYPES_PARAM = "nodeTypes";
93 private static final String KEYWORD_PARAM = "q";
94 private static final String ORDER_BY_PARAM = "orderBy";
95 private static final String OFFSET_PARAM = "offset";
96 private static final String LIMIT_PARAM = "limit";
97
98 private static final List<String> ENDPOINT_PARAMETERS = Collections.unmodifiableList(Arrays.asList(
99 NODE_TYPES_PARAM,
100 KEYWORD_PARAM,
101 ORDER_BY_PARAM,
102 OFFSET_PARAM,
103 LIMIT_PARAM
104 ));
105
106 private final Map<String, WorkspaceParameters> params;
107 private final Provider<Context> contextProvider;
108
109 @javax.ws.rs.core.Context
110 private UriInfo uriInfo;
111
112 @Inject
113 public JcrDeliveryEndpoint(JcrDeliveryEndpointDefinition endpointDefinition, Provider<Context> contextProvider) {
114 super(endpointDefinition);
115 this.contextProvider = contextProvider;
116 this.params = getEndpointDefinition().getParams();
117 }
118
119
120
121
122 @GET
123 @Path("/{path:.*}")
124 @Produces({ MediaType.APPLICATION_JSON })
125 public Node readNode(
126 @PathParam(ENDPOINT_PARAM) String endpointPrefix,
127 @PathParam(PATH_PARAM) @DefaultValue("/") String path) throws RepositoryException {
128
129 WorkspaceParameters param = params.get(endpointPrefix);
130
131 if (param != null) {
132 String workspace = param.getWorkspace();
133 if (param.getWorkspace() == null) {
134
135 workspace = endpointPrefix;
136 }
137
138 return doSessionOperation(workspace, param.isBypassWorkspaceAcls(), new JCRSessionOp<Node>(workspace) {
139 @Override
140 public Node exec(Session session) throws RepositoryException {
141 String nodePath = PathUtil.createPath(param.getRootPath(), path);
142 Node node = session.getNode(nodePath);
143
144 if (param.getNodeTypes() != null && !param.getNodeTypes().isEmpty()) {
145 boolean matchingNodeType = false;
146 for (String nodeType : param.getNodeTypes()) {
147 if (NodeUtil.isNodeType(node, nodeType)) {
148 matchingNodeType = true;
149 break;
150 }
151 }
152 if (!matchingNodeType) {
153 throw new NotFoundException(String.format("Node '%s' does not match any configured node type", node));
154 }
155 }
156
157 FilteringContentDecoratorBuilder decorators = new FilteringContentDecoratorBuilder()
158 .childNodeTypes(param.getChildNodeTypes())
159 .depth(param.getDepth())
160 .includeSystemProperties(param.isIncludeSystemProperties())
161 .references(param.getReferences());
162
163 node = decorators.wrapNode(node);
164
165 return node;
166 }
167 });
168 }
169
170 throw new NotFoundException(String.format("No workspace-params entry for endpoint prefix '%s'", endpointPrefix));
171 }
172
173
174
175
176 @GET
177 @Produces({ MediaType.APPLICATION_JSON })
178 public NodesResult queryNodes(@PathParam(ENDPOINT_PARAM) String endpointPrefix,
179 @QueryParam(KEYWORD_PARAM) String keyword,
180 @QueryParam(ORDER_BY_PARAM) String orderByParam,
181 @QueryParam(OFFSET_PARAM) Long offsetParam,
182 @QueryParam(LIMIT_PARAM) Long limitParam) throws RepositoryException {
183
184 WorkspaceParameters param = params.get(endpointPrefix);
185
186 if (param != null) {
187 String workspace = param.getWorkspace();
188 if (param.getWorkspace() == null) {
189
190 workspace = endpointPrefix;
191 }
192
193 long offset = offsetParam == null ? 0 : offsetParam;
194 long limit = limitParam == null ? param.getLimit() : limitParam;
195 List<String> propertiesToOrderBy = StringUtils.isEmpty(orderByParam) ? Arrays.asList("@name asc") : Arrays.asList(StringUtils.split(orderByParam, ","));
196 Map<String, List<String>> filteringConditions = uriInfo.getQueryParameters().entrySet().stream()
197 .filter(entry -> !ENDPOINT_PARAMETERS.contains(entry.getKey()) && !entry.getValue().isEmpty())
198 .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue()));
199
200 NodeIterator results = doSessionOperation(workspace, param.isBypassWorkspaceAcls(), new JCRSessionOp<NodeIterator>(workspace) {
201 @Override
202 public NodeIterator exec(Session session) throws RepositoryException {
203 Workspace workspaceObj = session.getWorkspace();
204
205 Query query = QueryBuilder.inWorkspace(workspaceObj)
206 .rootPath(param.getRootPath())
207 .nodeTypes(param.getNodeTypes())
208 .keyword(keyword)
209 .conditions(filteringConditions)
210 .orderBy(propertiesToOrderBy)
211 .offset(offset)
212 .limit(limit)
213 .build();
214
215 QueryResult result = query.execute();
216 NodeIterator nodeIterator = result.getNodes();
217
218 FilteringContentDecoratorBuilder decorators = new FilteringContentDecoratorBuilder()
219 .childNodeTypes(param.getChildNodeTypes())
220 .depth(param.getDepth())
221 .includeSystemProperties(param.isIncludeSystemProperties())
222 .references(param.getReferences());
223
224 nodeIterator = decorators.wrapNodeIterator(nodeIterator);
225
226 return nodeIterator;
227 }
228 });
229
230 return new NodesResult(results, 0);
231 }
232
233 throw new NotFoundException(String.format("No workspace-params entry for endpoint prefix '%s'", endpointPrefix));
234 }
235
236 protected <R> R doSessionOperation(String workspace, boolean bypassWorkspaceAcls, JCRSessionOp<R> operation) throws RepositoryException {
237 if (bypassWorkspaceAcls) {
238 return MgnlContext.doInSystemContext(operation);
239 }
240
241 return operation.exec(contextProvider.get().getJCRSession(workspace));
242 }
243 }