From 5c6fc580b26c7491859f762eab4631732fb4ecba Mon Sep 17 00:00:00 2001 From: Stepan Miakchilo Date: Mon, 24 Feb 2025 00:48:16 +0300 Subject: [PATCH 1/3] [EAK-558] Made OptionProviderServlet return an empty array instead of HTTP 404 if no valid items to display --- .../servlets/OptionProviderServlet.java | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/exadel/aem/toolkit/core/optionprovider/servlets/OptionProviderServlet.java b/core/src/main/java/com/exadel/aem/toolkit/core/optionprovider/servlets/OptionProviderServlet.java index 8b9083921..6b60fae41 100644 --- a/core/src/main/java/com/exadel/aem/toolkit/core/optionprovider/servlets/OptionProviderServlet.java +++ b/core/src/main/java/com/exadel/aem/toolkit/core/optionprovider/servlets/OptionProviderServlet.java @@ -20,7 +20,6 @@ import javax.annotation.Nonnull; import javax.servlet.Servlet; import javax.servlet.ServletException; -import javax.servlet.http.HttpServletResponse; import org.apache.commons.collections4.CollectionUtils; import org.apache.sling.api.SlingHttpServletRequest; @@ -73,28 +72,28 @@ public class OptionProviderServlet extends SlingSafeMethodsServlet { * @param response {@code SlingHttpServletResponse} instance */ @Override - protected void doGet(@Nonnull SlingHttpServletRequest request, @Nonnull SlingHttpServletResponse response) throws ServletException, IOException { + protected void doGet(@Nonnull SlingHttpServletRequest request, @Nonnull SlingHttpServletResponse response) + throws ServletException, IOException { List options = optionProvider.getOptions(request); - if (CollectionUtils.isEmpty(options) && isJsonOutput(request)) { - response.setStatus(HttpServletResponse.SC_NOT_FOUND); + if (!isJsonOutput(request)) { + DataSource ds = new SimpleDataSource(options.iterator()); + request.setAttribute(DataSource.class.getName(), ds); return; } - if (isJsonOutput(request)) { - response.setContentType(CoreConstants.CONTENT_TYPE_JSON); - response.setCharacterEncoding(StandardCharsets.UTF_8.toString()); - try { - response.getWriter().print(getJsonOutput(options)); - } catch (JSONException | NullPointerException e) { - throw new ServletException(e); - } + response.setCharacterEncoding(StandardCharsets.UTF_8.toString()); + response.setContentType(CoreConstants.CONTENT_TYPE_JSON); + if (CollectionUtils.isEmpty(options)) { + response.getWriter().write(CoreConstants.ARRAY_OPENING + CoreConstants.ARRAY_CLOSING); return; } - - DataSource ds = new SimpleDataSource(options.iterator()); - request.setAttribute(DataSource.class.getName(), ds); + try { + response.getWriter().print(getJsonOutput(options)); + } catch (JSONException | NullPointerException e) { + throw new ServletException(e); + } } /** @@ -103,6 +102,9 @@ protected void doGet(@Nonnull SlingHttpServletRequest request, @Nonnull SlingHtt * @return True or false */ private static boolean isJsonOutput(SlingHttpServletRequest request) { + if (QUERY_VALUE_JSON.equalsIgnoreCase(request.getRequestPathInfo().getExtension())) { + return true; + } RequestParameter jsonParameter = request.getRequestParameter(QUERY_KEY_OUTPUT); if (jsonParameter == null) { return false; From c66b71c00f38126a1875544bcfd5a41e536d9207 Mon Sep 17 00:00:00 2001 From: Stepan Miakchilo Date: Mon, 24 Feb 2025 00:48:48 +0300 Subject: [PATCH 2/3] [EAK-558] Modified info on EToolbox Lists usage --- docs/content/authoring-tools/etoolbox-lists.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/content/authoring-tools/etoolbox-lists.md b/docs/content/authoring-tools/etoolbox-lists.md index a7e0a8d7a..ee2797f2a 100644 --- a/docs/content/authoring-tools/etoolbox-lists.md +++ b/docs/content/authoring-tools/etoolbox-lists.md @@ -126,4 +126,6 @@ public class MyComponent { ## Retrieving Lists' content via HTTP -You website's authoring logic may require fetching the content of a list via HTTP - e.g., with an AJAX request. EToolbox provides a servlet that delivers a flat array of items in JSON format. The data will be stripped of all the "system" properties like _jcr:created_, etc. Request it with the `.list.` selector and `.json` extension. Example: `/content/path/to/your/lists/sampleList.list.json`. +Your website's authoring logic may require fetching the content of a list via HTTP — e.g., with an AJAX request. This can be achieved with the OptionSource / [OptionProvider](../dev-tools/option-provider.md) servlet. Make a GET request to `/apps/etoolbox-authoring-kit/datasources/option-provider.json?path=`. + +By default, the servlet returns a JSON array of objects with `text` and `value` properties matching the same-named attributes of list items. You will probably want to modify the mapping. To say, a typical EToolbox List makes use of `jcr:title` and `value` attributes. Add `&textMember=jcr:title` to expose _jcr:title_. Similarly, you can alter the `valueMember` parameter, add some `attributeMembers`, etc. Please remember that if an item doesn't have a non-blank mapping for both _text_ and _value_, it won't be displayed. From 89a3f70564742b50628e332a124572ec6d40e787 Mon Sep 17 00:00:00 2001 From: Stepan Miakchilo Date: Mon, 24 Feb 2025 00:51:45 +0300 Subject: [PATCH 3/3] [EAK-558] Retired the dedicated Lists JSON servlet --- core/pom.xml | 4 - .../core/lists/servlets/ListsJsonServlet.java | 97 ------------------- .../lists/servlets/ListsJsonServletTest.java | 92 ------------------ pom.xml | 6 -- 4 files changed, 199 deletions(-) delete mode 100644 core/src/main/java/com/exadel/aem/toolkit/core/lists/servlets/ListsJsonServlet.java delete mode 100644 core/src/test/java/com/exadel/aem/toolkit/core/lists/servlets/ListsJsonServletTest.java diff --git a/core/pom.xml b/core/pom.xml index a6551a230..b8c85a616 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -114,10 +114,6 @@ com.google.code.findbugs jsr305 - - com.google.code.gson - gson - com.google.guava guava diff --git a/core/src/main/java/com/exadel/aem/toolkit/core/lists/servlets/ListsJsonServlet.java b/core/src/main/java/com/exadel/aem/toolkit/core/lists/servlets/ListsJsonServlet.java deleted file mode 100644 index ce33121f2..000000000 --- a/core/src/main/java/com/exadel/aem/toolkit/core/lists/servlets/ListsJsonServlet.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.exadel.aem.toolkit.core.lists.servlets; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import javax.annotation.Nonnull; -import javax.servlet.Servlet; - -import org.apache.http.HttpStatus; -import org.apache.sling.api.SlingHttpServletRequest; -import org.apache.sling.api.SlingHttpServletResponse; -import org.apache.sling.api.resource.Resource; -import org.apache.sling.api.servlets.HttpConstants; -import org.apache.sling.api.servlets.SlingSafeMethodsServlet; -import org.osgi.service.component.annotations.Component; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.google.gson.Gson; -import com.day.cq.wcm.api.Page; -import com.day.cq.wcm.api.PageManager; - -import com.exadel.aem.toolkit.core.CoreConstants; -import com.exadel.aem.toolkit.core.lists.ListConstants; -import com.exadel.aem.toolkit.core.lists.models.internal.ListItemModel; - -/** - * Delivers content of an Exadel Toolbox List as a JSON array - *

Note: This class is not a part of the public API and is subject to change. Do not use it in your own - * code

- */ -@Component( - service = Servlet.class, - property = { - "sling.servlet.resourceTypes=/apps/etoolbox-authoring-kit/lists/components/structure/page-list", - "sling.servlet.extensions=json", - "sling.servlet.methods=" + HttpConstants.METHOD_GET, - "sling.servlet.selectors=list" - } -) -public class ListsJsonServlet extends SlingSafeMethodsServlet { - - private static final Logger LOG = LoggerFactory.getLogger(ListsJsonServlet.class); - - private static final Gson GSON = new Gson(); - - /** - * Processes a GET request to deliver the content of an Exadel Toolbox List as a JSON array - * @param request {@code SlingHttpServletRequest} instance - * @param response {@code SlingHttpServletResponse} instance - * @throws IOException If failed to output to the response stream - */ - @Override - protected void doGet(@Nonnull SlingHttpServletRequest request, @Nonnull SlingHttpServletResponse response) throws IOException { - PageManager pageManager = request.getResourceResolver().adaptTo(PageManager.class); - Page page = pageManager != null ? pageManager.getContainingPage(request.getResource()) : null; - if (page == null) { - LOG.warn("Resource at {} cannot be adapted to a page", request.getResource().getPath()); - response.setStatus(HttpStatus.SC_NOT_FOUND); - return; - } - - Resource listResource = page.getContentResource().getChild("list"); - if (listResource == null) { - LOG.warn("Path {} does not belong to an EToolbox List", request.getResource().getPath()); - response.setStatus(HttpStatus.SC_NOT_FOUND); - return; - } - - List> result = new ArrayList<>(); - for (Resource item : listResource.getChildren()) { - if (!ListConstants.LIST_ITEM_RESOURCE_TYPE.equals(item.getResourceType())) { - continue; - } - ListItemModel listItemModel = item.adaptTo(ListItemModel.class); - if (listItemModel != null) { - result.add(listItemModel.getProperties()); - } - } - - response.setContentType(CoreConstants.CONTENT_TYPE_JSON); - response.getWriter().write(GSON.toJson(result)); - } -} diff --git a/core/src/test/java/com/exadel/aem/toolkit/core/lists/servlets/ListsJsonServletTest.java b/core/src/test/java/com/exadel/aem/toolkit/core/lists/servlets/ListsJsonServletTest.java deleted file mode 100644 index 3faea170a..000000000 --- a/core/src/test/java/com/exadel/aem/toolkit/core/lists/servlets/ListsJsonServletTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.exadel.aem.toolkit.core.lists.servlets; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpStatus; -import org.apache.sling.testing.mock.sling.ResourceResolverType; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import com.google.gson.Gson; -import io.wcm.testing.mock.aem.junit.AemContext; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import com.exadel.aem.toolkit.core.AemContextFactory; - -public class ListsJsonServletTest { - - private static final String SIMPLE_LIST_PATH = "/content/etoolbox-authoring-kit/lists/simpleList"; - private static final String CUSTOM_LIST_PATH = "/content/etoolbox-authoring-kit/lists/etoolbox-lists/customList"; - - @Rule - public AemContext context = AemContextFactory.newInstance(ResourceResolverType.JCR_OAK); - - private ListsJsonServlet servlet; - - @Before - public void setUp() { - servlet = new ListsJsonServlet(); - context.load().json("/com/exadel/aem/toolkit/core/lists/utils/simpleList.json", SIMPLE_LIST_PATH); - context.load().json("/com/exadel/aem/toolkit/core/lists/utils/customList.json", CUSTOM_LIST_PATH); - context.create().page("/content/etoolbox-authoring-kit/lists/dummy", StringUtils.EMPTY); - } - - @Test - public void shouldRetrieveSimpleJson() throws IOException { - context.request().setResource(context.resourceResolver().getResource(SIMPLE_LIST_PATH + "/jcr:content")); - servlet.doGet(context.request(), context.response()); - - assertEquals(HttpStatus.SC_OK, context.response().getStatus()); - String jsonResponse = context.response().getOutputAsString(); - - @SuppressWarnings("unchecked") - List> result = new Gson().fromJson(jsonResponse, List.class); - assertEquals(4, result.size()); - assertTrue(result.stream().allMatch(item -> item.containsKey("value"))); - } - - @Test - public void shouldRetrieveCustomJson() throws IOException { - context.request().setResource(context.resourceResolver().getResource(CUSTOM_LIST_PATH + "/jcr:content")); - servlet.doGet(context.request(), context.response()); - - assertEquals(HttpStatus.SC_OK, context.response().getStatus()); - String jsonResponse = context.response().getOutputAsString(); - - @SuppressWarnings("unchecked") - List> result = new Gson().fromJson(jsonResponse, List.class); - assertEquals(2, result.size()); - assertTrue(result.stream().allMatch(item -> item.containsKey("textValue"))); - assertTrue(result.stream().allMatch(item -> item.containsKey("booleanValue"))); - } - - @Test - public void shouldReturn404ForNonList() throws IOException { - context.request().setResource(context.resourceResolver().getResource("/content/etoolbox-authoring-kit/lists")); - servlet.doGet(context.request(), context.response()); - - assertEquals(HttpStatus.SC_NOT_FOUND, context.response().getStatus()); - - context.request().setResource(context.resourceResolver().getResource("/content/etoolbox-authoring-kit/lists/dummy")); - servlet.doGet(context.request(), context.response()); - - assertEquals(HttpStatus.SC_NOT_FOUND, context.response().getStatus()); - } -} diff --git a/pom.xml b/pom.xml index a16ab2f06..63a6f5413 100644 --- a/pom.xml +++ b/pom.xml @@ -489,12 +489,6 @@ 3.0.2 provided
- - com.google.code.gson - gson - 2.11.0 - test - com.codeborne selenide