From 317310ef0b4cd2694cb0d30ad767811a078f7bd4 Mon Sep 17 00:00:00 2001 From: Jens Meichler Date: Wed, 19 Nov 2025 11:48:17 +0100 Subject: [PATCH 01/10] feat: add sort option commands --- .../io/constructor/client/ConstructorIO.java | 198 ++++++++++++++++++ .../constructor/client/SortOptionRequest.java | 64 ++++++ .../constructor/client/models/SortOption.java | 109 ++++++++++ .../client/models/SortOptionsResponse.java | 42 ++++ 4 files changed, 413 insertions(+) create mode 100644 constructorio-client/src/main/java/io/constructor/client/SortOptionRequest.java create mode 100644 constructorio-client/src/main/java/io/constructor/client/models/SortOption.java create mode 100644 constructorio-client/src/main/java/io/constructor/client/models/SortOptionsResponse.java diff --git a/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java b/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java index b71d8cba..825ab2bf 100644 --- a/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java +++ b/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java @@ -3107,4 +3107,202 @@ public String deleteFacetOptionConfiguration( facetOptionConfigurationRequest.getFacetOptionConfiguration().getValue(), facetOptionConfigurationRequest.getSection()); } + + /** + * Creates a sort option + * + * @param sortOptionRequest the sort option request + * @return returns the created sort option as JSON string + * @throws ConstructorException if the request is invalid + */ + public String createSortOption(SortOptionRequest sortOptionRequest) + throws ConstructorException { + try { + HttpUrl url = this.makeUrl(Arrays.asList("v1", "sort_option")); + url = + url.newBuilder() + .addQueryParameter("section", sortOptionRequest.getSection()) + .build(); + + String params = new Gson().toJson(sortOptionRequest.getSortOption()); + RequestBody body = + RequestBody.create(params, MediaType.parse("application/json; charset=utf-8")); + Request request = this.makeAuthorizedRequestBuilder().url(url).post(body).build(); + + Response response = client.newCall(request).execute(); + + return getResponseBody(response); + } catch (Exception exception) { + throw new ConstructorException(exception); + } + } + + /** + * Updates a sort option (creates or replaces) + * + * @param sortOptionRequest the sort option request + * @return returns the updated sort option as JSON string + * @throws ConstructorException if the request is invalid + */ + public String updateSortOption(SortOptionRequest sortOptionRequest) + throws ConstructorException { + if (sortOptionRequest.getSortOption().getSortBy() == null + || sortOptionRequest.getSortOption().getSortBy().trim().isEmpty()) { + throw new IllegalArgumentException("sortBy is required for update"); + } + if (sortOptionRequest.getSortOption().getSortOrder() == null + || sortOptionRequest.getSortOption().getSortOrder().trim().isEmpty()) { + throw new IllegalArgumentException("sortOrder is required for update"); + } + + try { + HttpUrl url = + this.makeUrl( + Arrays.asList( + "v1", + "sort_option", + sortOptionRequest.getSortOption().getSortBy(), + sortOptionRequest.getSortOption().getSortOrder())); + url = + url.newBuilder() + .addQueryParameter("section", sortOptionRequest.getSection()) + .build(); + + Map bodyParams = new HashMap(); + if (sortOptionRequest.getSortOption().getDisplayName() != null) { + bodyParams.put("display_name", sortOptionRequest.getSortOption().getDisplayName()); + } + if (sortOptionRequest.getSortOption().getPathInMetadata() != null) { + bodyParams.put( + "path_in_metadata", sortOptionRequest.getSortOption().getPathInMetadata()); + } + if (sortOptionRequest.getSortOption().getPosition() != null) { + bodyParams.put("position", sortOptionRequest.getSortOption().getPosition()); + } + if (sortOptionRequest.getSortOption().getHidden() != null) { + bodyParams.put("hidden", sortOptionRequest.getSortOption().getHidden()); + } + + String params = new Gson().toJson(bodyParams); + RequestBody body = + RequestBody.create(params, MediaType.parse("application/json; charset=utf-8")); + Request request = this.makeAuthorizedRequestBuilder().url(url).put(body).build(); + + Response response = client.newCall(request).execute(); + + return getResponseBody(response); + } catch (Exception exception) { + throw new ConstructorException(exception); + } + } + + /** + * Deletes sort options + * + * @param sortBy the sort by field + * @param sortOrder the sort order (ascending or descending) + * @param section the section to which the sort option belongs + * @return returns the deleted sort options as JSON string + * @throws ConstructorException if the request is invalid + */ + public String deleteSortOptions(String sortBy, String sortOrder, String section) + throws ConstructorException { + if (sortBy == null || sortBy.trim().isEmpty()) { + throw new IllegalArgumentException("sortBy is required"); + } + if (sortOrder == null || sortOrder.trim().isEmpty()) { + throw new IllegalArgumentException("sortOrder is required"); + } + + try { + HttpUrl url = + this.makeUrl(Arrays.asList("v1", "sort_options")) + .newBuilder() + .addQueryParameter("section", section) + .build(); + + Map sortOptionParams = new HashMap(); + sortOptionParams.put("sort_by", sortBy); + sortOptionParams.put("sort_order", sortOrder); + + Map bodyParams = new HashMap(); + bodyParams.put("sort_options", Arrays.asList(sortOptionParams)); + + String jsonParams = new Gson().toJson(bodyParams); + RequestBody body = + RequestBody.create( + jsonParams, MediaType.parse("application/json; charset=utf-8")); + Request request = this.makeAuthorizedRequestBuilder().url(url).delete(body).build(); + + Response response = client.newCall(request).execute(); + + return getResponseBody(response); + } catch (Exception exception) { + throw new ConstructorException(exception); + } + } + + /** + * Deletes sort options with default section "Products" + * + * @param sortBy the sort by field + * @param sortOrder the sort order (ascending or descending) + * @return returns the deleted sort options as JSON string + * @throws ConstructorException if the request is invalid + */ + public String deleteSortOptions(String sortBy, String sortOrder) throws ConstructorException { + return deleteSortOptions(sortBy, sortOrder, "Products"); + } + + /** + * Retrieves all sort options + * + * @param section the section to retrieve sort options for + * @param sortBy optional filter by sort by field (can be null) + * @return returns the sort options response + * @throws ConstructorException if the request is invalid + */ + public SortOptionsResponse retrieveSortOptions(String section, String sortBy) + throws ConstructorException { + try { + HttpUrl url = this.makeUrl(Arrays.asList("v1", "sort_options")); + HttpUrl.Builder urlBuilder = url.newBuilder().addQueryParameter("section", section); + + if (sortBy != null && !sortBy.trim().isEmpty()) { + urlBuilder.addQueryParameter("sort_by", sortBy); + } + + url = urlBuilder.build(); + + Request request = this.makeAuthorizedRequestBuilder().url(url).get().build(); + + Response response = clientWithRetry.newCall(request).execute(); + String body = getResponseBody(response); + + return new Gson().fromJson(body, SortOptionsResponse.class); + } catch (Exception exception) { + throw new ConstructorException(exception); + } + } + + /** + * Retrieves all sort options with default section "Products" + * + * @param sortBy optional filter by sort by field (can be null) + * @return returns the sort options response + * @throws ConstructorException if the request is invalid + */ + public SortOptionsResponse retrieveSortOptions(String sortBy) throws ConstructorException { + return retrieveSortOptions("Products", sortBy); + } + + /** + * Retrieves all sort options with default section "Products" and no filter + * + * @return returns the sort options response + * @throws ConstructorException if the request is invalid + */ + public SortOptionsResponse retrieveSortOptions() throws ConstructorException { + return retrieveSortOptions("Products", null); + } } diff --git a/constructorio-client/src/main/java/io/constructor/client/SortOptionRequest.java b/constructorio-client/src/main/java/io/constructor/client/SortOptionRequest.java new file mode 100644 index 00000000..0fbf1ded --- /dev/null +++ b/constructorio-client/src/main/java/io/constructor/client/SortOptionRequest.java @@ -0,0 +1,64 @@ +package io.constructor.client; + +import io.constructor.client.models.SortOption; + +/** Constructor.io Sort Option Request */ +public class SortOptionRequest { + private SortOption sortOption; + private String section; + + /** + * Creates a sort option request + * + * @param sortOption the sort option to be created/updated + * @param section the section to which the sort option belongs + */ + public SortOptionRequest(SortOption sortOption, String section) { + if (sortOption == null) { + throw new IllegalArgumentException("sortOption is required"); + } + if (section == null) { + throw new IllegalArgumentException("section is required"); + } + + this.sortOption = sortOption; + this.section = section; + } + + /** + * Creates a sort option request with default section "Products" + * + * @param sortOption the sort option to be created/updated + */ + public SortOptionRequest(SortOption sortOption) { + this(sortOption, "Products"); + } + + /** + * @return the sort option + */ + public SortOption getSortOption() { + return sortOption; + } + + /** + * @param sortOption the sort option to set + */ + public void setSortOption(SortOption sortOption) { + this.sortOption = sortOption; + } + + /** + * @return the section + */ + public String getSection() { + return section; + } + + /** + * @param section the section to set + */ + public void setSection(String section) { + this.section = section; + } +} \ No newline at end of file diff --git a/constructorio-client/src/main/java/io/constructor/client/models/SortOption.java b/constructorio-client/src/main/java/io/constructor/client/models/SortOption.java new file mode 100644 index 00000000..646b4cbc --- /dev/null +++ b/constructorio-client/src/main/java/io/constructor/client/models/SortOption.java @@ -0,0 +1,109 @@ +package io.constructor.client.models; + +import com.google.gson.annotations.SerializedName; + +/** Constructor.io Sort Option ... uses Gson/Reflection to load data in */ +public class SortOption { + + @SerializedName("display_name") + private String displayName; + + @SerializedName("sort_by") + private String sortBy; + + @SerializedName("sort_order") + private String sortOrder; + + @SerializedName("path_in_metadata") + private String pathInMetadata; + + @SerializedName("position") + private Integer position; + + @SerializedName("hidden") + private Boolean hidden; + + /** + * @return the display name + */ + public String getDisplayName() { + return displayName; + } + + /** + * @param displayName the display name to set + */ + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + /** + * @return the sort by field + */ + public String getSortBy() { + return sortBy; + } + + /** + * @param sortBy the sort by field to set + */ + public void setSortBy(String sortBy) { + this.sortBy = sortBy; + } + + /** + * @return the sort order (ascending or descending) + */ + public String getSortOrder() { + return sortOrder; + } + + /** + * @param sortOrder the sort order to set (ascending or descending) + */ + public void setSortOrder(String sortOrder) { + this.sortOrder = sortOrder; + } + + /** + * @return the path in metadata + */ + public String getPathInMetadata() { + return pathInMetadata; + } + + /** + * @param pathInMetadata the path in metadata to set + */ + public void setPathInMetadata(String pathInMetadata) { + this.pathInMetadata = pathInMetadata; + } + + /** + * @return the position + */ + public Integer getPosition() { + return position; + } + + /** + * @param position the position to set + */ + public void setPosition(Integer position) { + this.position = position; + } + + /** + * @return whether the sort option is hidden + */ + public Boolean getHidden() { + return hidden; + } + + /** + * @param hidden whether the sort option is hidden + */ + public void setHidden(Boolean hidden) { + this.hidden = hidden; + } +} \ No newline at end of file diff --git a/constructorio-client/src/main/java/io/constructor/client/models/SortOptionsResponse.java b/constructorio-client/src/main/java/io/constructor/client/models/SortOptionsResponse.java new file mode 100644 index 00000000..b4f6b6e0 --- /dev/null +++ b/constructorio-client/src/main/java/io/constructor/client/models/SortOptionsResponse.java @@ -0,0 +1,42 @@ +package io.constructor.client.models; + +import com.google.gson.annotations.SerializedName; +import java.util.List; + +/** Constructor.io Sort Options Response ... uses Gson/Reflection to load data in */ +public class SortOptionsResponse { + + @SerializedName("total_count") + private int totalCount; + + @SerializedName("sort_options") + private List sortOptions; + + /** + * @return the total count + */ + public int getTotalCount() { + return totalCount; + } + + /** + * @param totalCount the total count to set + */ + public void setTotalCount(int totalCount) { + this.totalCount = totalCount; + } + + /** + * @return the sort options + */ + public List getSortOptions() { + return sortOptions; + } + + /** + * @param sortOptions the sort options to set + */ + public void setSortOptions(List sortOptions) { + this.sortOptions = sortOptions; + } +} From 42a8ea4bb196605e60140614d2b51d65817f1594 Mon Sep 17 00:00:00 2001 From: Jens Meichler Date: Wed, 19 Nov 2025 11:52:23 +0100 Subject: [PATCH 02/10] test: add sort option unit tests --- .../client/ConstructorIOSortOptionsTest.java | 285 ++++++++++++++++++ .../client/SortOptionRequestTest.java | 74 +++++ .../io/constructor/client/SortOptionTest.java | 85 ++++++ .../client/SortOptionsResponseTest.java | 94 ++++++ 4 files changed, 538 insertions(+) create mode 100644 constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java create mode 100644 constructorio-client/src/test/java/io/constructor/client/SortOptionRequestTest.java create mode 100644 constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java create mode 100644 constructorio-client/src/test/java/io/constructor/client/SortOptionsResponseTest.java diff --git a/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java new file mode 100644 index 00000000..f3e84847 --- /dev/null +++ b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java @@ -0,0 +1,285 @@ +package io.constructor.client; + +import static org.junit.Assert.*; + +import com.google.gson.Gson; +import io.constructor.client.models.SortOption; +import io.constructor.client.models.SortOptionsResponse; +import java.util.ArrayList; +import org.json.JSONObject; +import org.junit.AfterClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class ConstructorIOSortOptionsTest { + + private static String token = System.getenv("TEST_API_TOKEN"); + private static String apiKey = System.getenv("TEST_CATALOG_API_KEY"); + private static ArrayList sortOptionsToCleanup = new ArrayList<>(); + + private void addSortOptionToCleanupArray(String sortBy, String sortOrder, String section) { + if (section == null) { + section = "Products"; + } + sortOptionsToCleanup.add(sortBy + "|" + sortOrder + "|" + section); + } + + private void addSortOptionToCleanupArray(String sortBy, String sortOrder) { + addSortOptionToCleanupArray(sortBy, sortOrder, "Products"); + } + + @AfterClass + public static void cleanupSortOptions() throws ConstructorException { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + for (String sortOptionKey : sortOptionsToCleanup) { + String[] parts = sortOptionKey.split("\\|"); + String sortBy = parts[0]; + String sortOrder = parts[1]; + String section = parts[2]; + + try { + constructor.deleteSortOptions(sortBy, sortOrder, section); + } catch (ConstructorException e) { + System.err.println( + "Warning: Failed to clean up sort option: " + + sortBy + + " " + + sortOrder); + } + } + } + + @Rule public ExpectedException thrown = ExpectedException.none(); + + @Test + public void createSortOptionShouldReturn() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + SortOption sortOption = new SortOption(); + sortOption.setDisplayName("Test Price Sort"); + sortOption.setPathInMetadata("test_price"); + sortOption.setHidden(false); + sortOption.setSortBy("test_price"); + sortOption.setSortOrder("ascending"); + + SortOptionRequest request = new SortOptionRequest(sortOption, "Products"); + + String response = constructor.createSortOption(request); + JSONObject jsonObj = new JSONObject(response); + + assertEquals(jsonObj.get("display_name"), "Test Price Sort"); + assertEquals(jsonObj.get("sort_by"), "test_price"); + assertEquals(jsonObj.get("sort_order"), "ascending"); + assertEquals(jsonObj.get("path_in_metadata"), "test_price"); + assertEquals(jsonObj.get("hidden"), false); + + addSortOptionToCleanupArray("test_price", "ascending"); + } + + @Test(expected = ConstructorException.class) + public void testCreateSortOptionWithNullRequest() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + constructor.createSortOption(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateSortOptionWithNullSortOption() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + SortOptionRequest request = new SortOptionRequest(null, "Products"); + constructor.createSortOption(request); + } + + @Test + public void testCreateSortOptionWithDifferentSection() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + SortOption sortOption = new SortOption(); + sortOption.setDisplayName("Custom Section Sort"); + sortOption.setPathInMetadata("custom_field"); + sortOption.setSortBy("custom_field"); + sortOption.setSortOrder("descending"); + sortOption.setHidden(true); + + SortOptionRequest request = new SortOptionRequest(sortOption, "Search Suggestions"); + + String response = constructor.createSortOption(request); + JSONObject jsonObj = new JSONObject(response); + + assertEquals("custom_field", jsonObj.getString("sort_by")); + assertEquals("descending", jsonObj.getString("sort_order")); + + addSortOptionToCleanupArray("custom_field", "descending", "Search Suggestions"); + } + + @Test + public void testUpdateSortOption() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create a sort option first + SortOption createOption = new SortOption(); + createOption.setDisplayName("Original Name"); + createOption.setPathInMetadata("update_test"); + createOption.setSortBy("update_test"); + createOption.setSortOrder("ascending"); + createOption.setHidden(false); + + constructor.createSortOption(new SortOptionRequest(createOption, "Products")); + + // Update the sort option + SortOption updateOption = new SortOption(); + updateOption.setDisplayName("Updated Name"); + updateOption.setPathInMetadata("update_test_v2"); + updateOption.setSortBy("update_test"); + updateOption.setSortOrder("ascending"); + updateOption.setHidden(true); + updateOption.setPosition(5); + + SortOptionRequest updateRequest = new SortOptionRequest(updateOption, "Products"); + String updateResponse = constructor.updateSortOption(updateRequest); + JSONObject jsonObj = new JSONObject(updateResponse); + + assertEquals("Updated Name", jsonObj.getString("display_name")); + assertEquals("update_test_v2", jsonObj.getString("path_in_metadata")); + assertEquals(true, jsonObj.getBoolean("hidden")); + assertEquals(5, jsonObj.getInt("position")); + + addSortOptionToCleanupArray("update_test", "ascending"); + } + + @Test(expected = IllegalArgumentException.class) + public void testUpdateSortOptionWithoutSortBy() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + SortOption sortOption = new SortOption(); + sortOption.setDisplayName("Test"); + sortOption.setSortOrder("ascending"); + + SortOptionRequest request = new SortOptionRequest(sortOption, "Products"); + constructor.updateSortOption(request); + } + + @Test(expected = IllegalArgumentException.class) + public void testUpdateSortOptionWithoutSortOrder() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + SortOption sortOption = new SortOption(); + sortOption.setDisplayName("Test"); + sortOption.setSortBy("test_field"); + + SortOptionRequest request = new SortOptionRequest(sortOption, "Products"); + constructor.updateSortOption(request); + } + + @Test + public void testDeleteSortOptionWithSortByAndSortOrder() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create a sort option first + SortOption sortOption = new SortOption(); + sortOption.setDisplayName("Delete Test"); + sortOption.setPathInMetadata("delete_test"); + sortOption.setSortBy("delete_test"); + sortOption.setSortOrder("ascending"); + + constructor.createSortOption(new SortOptionRequest(sortOption, "Products")); + + // Delete the sort option + String deleteResponse = constructor.deleteSortOptions("delete_test", "ascending", "Products"); + JSONObject jsonObj = new JSONObject(deleteResponse); + + assertTrue("Response indicates success", jsonObj.has("message") || jsonObj.has("sort_by")); + } + + @Test + public void testDeleteSortOptionWithDefaultSection() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create a sort option first + SortOption sortOption = new SortOption(); + sortOption.setDisplayName("Delete Default Section"); + sortOption.setPathInMetadata("delete_default"); + sortOption.setSortBy("delete_default"); + sortOption.setSortOrder("descending"); + + constructor.createSortOption(new SortOptionRequest(sortOption, "Products")); + + // Delete the sort option using default section + String deleteResponse = constructor.deleteSortOptions("delete_default", "descending"); + JSONObject jsonObj = new JSONObject(deleteResponse); + + assertTrue("Response indicates success", jsonObj.has("message") || jsonObj.has("sort_by")); + } + + @Test(expected = IllegalArgumentException.class) + public void testDeleteSortOptionWithEmptySortBy() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + constructor.deleteSortOptions("", "ascending", "Products"); + } + + @Test(expected = IllegalArgumentException.class) + public void testDeleteSortOptionWithEmptySortOrder() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + constructor.deleteSortOptions("test", "", "Products"); + } + + @Test + public void testRetrieveSortOptions() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + SortOptionsResponse response = constructor.retrieveSortOptions(); + + assertNotNull(response); + assertNotNull(response.getSortOptions()); + assertTrue(response.getTotalCount() >= 0); + } + + @Test + public void testRetrieveSortOptionsWithSection() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + SortOptionsResponse response = constructor.retrieveSortOptions("Products", null); + + assertNotNull(response); + assertNotNull(response.getSortOptions()); + assertTrue(response.getTotalCount() >= 0); + } + + @Test + public void testRetrieveSortOptionsWithFilter() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create a sort option to ensure there's something to retrieve + SortOption sortOption = new SortOption(); + sortOption.setDisplayName("Retrieve Filter Test"); + sortOption.setPathInMetadata("retrieve_filter_test"); + sortOption.setSortBy("retrieve_filter_test"); + sortOption.setSortOrder("ascending"); + + constructor.createSortOption(new SortOptionRequest(sortOption, "Products")); + addSortOptionToCleanupArray("retrieve_filter_test", "ascending"); + + // Retrieve with filter + SortOptionsResponse response = + constructor.retrieveSortOptions("Products", "retrieve_filter_test"); + + assertNotNull(response); + assertNotNull(response.getSortOptions()); + + // If there are results, verify they match the filter + if (response.getTotalCount() > 0) { + for (SortOption option : response.getSortOptions()) { + assertEquals("retrieve_filter_test", option.getSortBy()); + } + } + } + + @Test + public void testSortOptionDefaultValues() { + SortOption sortOption = new SortOption(); + assertNull("Position should default to null", sortOption.getPosition()); + assertNull("Hidden should default to null", sortOption.getHidden()); + assertNull("Display name should default to null", sortOption.getDisplayName()); + } +} diff --git a/constructorio-client/src/test/java/io/constructor/client/SortOptionRequestTest.java b/constructorio-client/src/test/java/io/constructor/client/SortOptionRequestTest.java new file mode 100644 index 00000000..5eb9fca8 --- /dev/null +++ b/constructorio-client/src/test/java/io/constructor/client/SortOptionRequestTest.java @@ -0,0 +1,74 @@ +package io.constructor.client; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import io.constructor.client.models.SortOption; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class SortOptionRequestTest { + @Rule public ExpectedException thrown = ExpectedException.none(); + + @Test + public void newSortOptionRequestShouldReturnWithDefaultSection() { + SortOption sortOption = new SortOption(); + sortOption.setDisplayName("Price"); + sortOption.setSortBy("price"); + sortOption.setSortOrder("ascending"); + + SortOptionRequest request = new SortOptionRequest(sortOption); + assertNotNull(request); + assertEquals(request.getSection(), "Products"); + assertEquals(request.getSortOption(), sortOption); + } + + @Test + public void newSortOptionRequestShouldReturnWithCustomSection() { + SortOption sortOption = new SortOption(); + sortOption.setDisplayName("Price"); + sortOption.setSortBy("price"); + sortOption.setSortOrder("ascending"); + + SortOptionRequest request = new SortOptionRequest(sortOption, "Search Suggestions"); + assertNotNull(request); + assertEquals(request.getSection(), "Search Suggestions"); + assertEquals(request.getSortOption(), sortOption); + } + + @Test + public void sortOptionRequestSettersWork() { + SortOption sortOption1 = new SortOption(); + sortOption1.setDisplayName("Price"); + sortOption1.setSortBy("price"); + + SortOption sortOption2 = new SortOption(); + sortOption2.setDisplayName("Name"); + sortOption2.setSortBy("name"); + + SortOptionRequest request = new SortOptionRequest(sortOption1); + request.setSortOption(sortOption2); + request.setSection("Custom Section"); + + assertEquals(request.getSortOption(), sortOption2); + assertEquals(request.getSection(), "Custom Section"); + } + + @Test + public void newSortOptionRequestShouldThrowExceptionWithNullSortOption() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("sortOption is required"); + new SortOptionRequest(null); + } + + @Test + public void newSortOptionRequestShouldThrowExceptionWithNullSection() { + SortOption sortOption = new SortOption(); + sortOption.setDisplayName("Price"); + + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("section is required"); + new SortOptionRequest(sortOption, null); + } +} diff --git a/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java b/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java new file mode 100644 index 00000000..a0efa0f1 --- /dev/null +++ b/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java @@ -0,0 +1,85 @@ +package io.constructor.client; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import com.google.gson.Gson; +import io.constructor.client.models.SortOption; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class SortOptionTest { + @Rule public ExpectedException thrown = ExpectedException.none(); + + @Test + public void sortOptionDeserialization() throws Exception { + String json = + "{" + + "\"display_name\": \"Preis\"," + + "\"path_in_metadata\": \"price_min\"," + + "\"position\": null," + + "\"hidden\": true," + + "\"sort_by\": \"price\"," + + "\"sort_order\": \"ascending\"" + + "}"; + + SortOption sortOption = new Gson().fromJson(json, SortOption.class); + assertEquals(sortOption.getDisplayName(), "Preis"); + assertEquals(sortOption.getPathInMetadata(), "price_min"); + assertNull(sortOption.getPosition()); + assertEquals(sortOption.getHidden(), Boolean.TRUE); + assertEquals(sortOption.getSortBy(), "price"); + assertEquals(sortOption.getSortOrder(), "ascending"); + } + + @Test + public void sortOptionSerialization() throws Exception { + SortOption sortOption = new SortOption(); + sortOption.setDisplayName("Preis"); + sortOption.setPathInMetadata("price_min"); + sortOption.setPosition(1); + sortOption.setHidden(true); + sortOption.setSortBy("price"); + sortOption.setSortOrder("descending"); + + String json = new Gson().toJson(sortOption); + + // Verify the JSON contains the expected fields + SortOption deserialized = new Gson().fromJson(json, SortOption.class); + assertEquals(deserialized.getDisplayName(), "Preis"); + assertEquals(deserialized.getPathInMetadata(), "price_min"); + assertEquals(deserialized.getPosition(), Integer.valueOf(1)); + assertEquals(deserialized.getHidden(), Boolean.TRUE); + assertEquals(deserialized.getSortBy(), "price"); + assertEquals(deserialized.getSortOrder(), "descending"); + } + + @Test + public void sortOptionDefaultValues() { + SortOption sortOption = new SortOption(); + assertNull("Display name should default to null", sortOption.getDisplayName()); + assertNull("Path in metadata should default to null", sortOption.getPathInMetadata()); + assertNull("Position should default to null", sortOption.getPosition()); + assertNull("Hidden should default to null", sortOption.getHidden()); + assertNull("Sort by should default to null", sortOption.getSortBy()); + assertNull("Sort order should default to null", sortOption.getSortOrder()); + } + + @Test + public void sortOptionWithNullPosition() throws Exception { + String json = + "{" + + "\"display_name\": \"Name\"," + + "\"sort_by\": \"name\"," + + "\"sort_order\": \"ascending\"," + + "\"position\": null," + + "\"hidden\": false" + + "}"; + + SortOption sortOption = new Gson().fromJson(json, SortOption.class); + assertEquals(sortOption.getDisplayName(), "Name"); + assertNull(sortOption.getPosition()); + assertEquals(sortOption.getHidden(), Boolean.FALSE); + } +} diff --git a/constructorio-client/src/test/java/io/constructor/client/SortOptionsResponseTest.java b/constructorio-client/src/test/java/io/constructor/client/SortOptionsResponseTest.java new file mode 100644 index 00000000..7d91c23e --- /dev/null +++ b/constructorio-client/src/test/java/io/constructor/client/SortOptionsResponseTest.java @@ -0,0 +1,94 @@ +package io.constructor.client; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import com.google.gson.Gson; +import io.constructor.client.models.SortOption; +import io.constructor.client.models.SortOptionsResponse; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class SortOptionsResponseTest { + @Rule public ExpectedException thrown = ExpectedException.none(); + + @Test + public void sortOptionsResponseDeserialization() throws Exception { + String json = + "{" + + "\"total_count\": 2," + + "\"sort_options\": [" + + " {" + + " \"display_name\": \"Preis\"," + + " \"path_in_metadata\": \"price_min\"," + + " \"position\": null," + + " \"hidden\": true," + + " \"sort_by\": \"price\"," + + " \"sort_order\": \"ascending\"" + + " }," + + " {" + + " \"display_name\": \"Preis\"," + + " \"path_in_metadata\": \"price_min\"," + + " \"position\": 1," + + " \"hidden\": false," + + " \"sort_by\": \"price\"," + + " \"sort_order\": \"descending\"" + + " }" + + "]" + + "}"; + + SortOptionsResponse response = new Gson().fromJson(json, SortOptionsResponse.class); + assertNotNull(response); + assertEquals(response.getTotalCount(), 2); + assertNotNull(response.getSortOptions()); + assertEquals(response.getSortOptions().size(), 2); + + // Check first sort option + SortOption first = response.getSortOptions().get(0); + assertEquals(first.getDisplayName(), "Preis"); + assertEquals(first.getPathInMetadata(), "price_min"); + assertNull(first.getPosition()); + assertEquals(first.getHidden(), Boolean.TRUE); + assertEquals(first.getSortBy(), "price"); + assertEquals(first.getSortOrder(), "ascending"); + + // Check second sort option + SortOption second = response.getSortOptions().get(1); + assertEquals(second.getDisplayName(), "Preis"); + assertEquals(second.getPathInMetadata(), "price_min"); + assertEquals(second.getPosition(), Integer.valueOf(1)); + assertEquals(second.getHidden(), Boolean.FALSE); + assertEquals(second.getSortBy(), "price"); + assertEquals(second.getSortOrder(), "descending"); + } + + @Test + public void sortOptionsResponseWithEmptyList() throws Exception { + String json = "{" + "\"total_count\": 0," + "\"sort_options\": []" + "}"; + + SortOptionsResponse response = new Gson().fromJson(json, SortOptionsResponse.class); + assertNotNull(response); + assertEquals(response.getTotalCount(), 0); + assertNotNull(response.getSortOptions()); + assertEquals(response.getSortOptions().size(), 0); + } + + @Test + public void sortOptionsResponseSettersWork() { + SortOptionsResponse response = new SortOptionsResponse(); + response.setTotalCount(5); + + java.util.ArrayList sortOptions = new java.util.ArrayList(); + SortOption sortOption = new SortOption(); + sortOption.setDisplayName("Test"); + sortOptions.add(sortOption); + + response.setSortOptions(sortOptions); + + assertEquals(response.getTotalCount(), 5); + assertEquals(response.getSortOptions().size(), 1); + assertEquals(response.getSortOptions().get(0).getDisplayName(), "Test"); + } +} From 4e7a71ba417a69dbd1a09132861e3eecbce58972 Mon Sep 17 00:00:00 2001 From: Jens Meichler Date: Wed, 19 Nov 2025 11:54:24 +0100 Subject: [PATCH 03/10] docs: add sort option management to README.md --- README.md | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/README.md b/README.md index 8714c8e0..ed3e2f0d 100644 --- a/README.md +++ b/README.md @@ -401,6 +401,86 @@ Task response = constructor.task(request); String response = constructor.taskAsJSON(request); ``` +# Managing Sort Options + +Sort options allow you to define custom sorting strategies for search and browse results. You can create, update, delete, and retrieve sort options for your catalog. + +## Creating a Sort Option + +To create a new sort option, you will need to create a `SortOption` object with the required fields and wrap it in a `SortOptionRequest`. + +```java +// Create a SortOption with the required fields +SortOption sortOption = new SortOption(); +sortOption.setDisplayName("Price"); +sortOption.setPathInMetadata("price_min"); +sortOption.setSortBy("price"); +sortOption.setSortOrder("ascending"); +sortOption.setHidden(false); + +// Create a SortOptionRequest with the sort option and section +SortOptionRequest request = new SortOptionRequest(sortOption, "Products"); + +// Create the sort option +String response = constructor.createSortOption(request); +``` + +## Updating a Sort Option + +To update an existing sort option (or create it if it doesn't exist), use the `updateSortOption` method. The sort option is identified by the combination of `sortBy` and `sortOrder`. + +```java +// Create a SortOption with updated fields +SortOption sortOption = new SortOption(); +sortOption.setDisplayName("Price (Low to High)"); +sortOption.setPathInMetadata("price_min"); +sortOption.setSortBy("price"); +sortOption.setSortOrder("ascending"); +sortOption.setHidden(false); +sortOption.setPosition(1); + +// Create a SortOptionRequest +SortOptionRequest request = new SortOptionRequest(sortOption, "Products"); + +// Update the sort option +String response = constructor.updateSortOption(request); +``` + +## Deleting Sort Options + +To delete a sort option, you need to specify the `sortBy` and `sortOrder` fields that identify it. + +```java +// Delete a sort option by sortBy and sortOrder +String response = constructor.deleteSortOptions("price", "ascending", "Products"); + +// Or use the default section "Products" +String response = constructor.deleteSortOptions("price", "ascending"); +``` + +## Retrieving Sort Options + +To retrieve all sort options for a section, you can use the `retrieveSortOptions` method. You can optionally filter by a specific `sortBy` field. + +```java +// Retrieve all sort options for the default "Products" section +SortOptionsResponse response = constructor.retrieveSortOptions(); + +// Retrieve sort options for a specific section +SortOptionsResponse response = constructor.retrieveSortOptions("Products", null); + +// Retrieve sort options filtered by sortBy field +SortOptionsResponse response = constructor.retrieveSortOptions("Products", "price"); + +// Access the results +int totalCount = response.getTotalCount(); +List sortOptions = response.getSortOptions(); + +for (SortOption option : sortOptions) { + System.out.println(option.getDisplayName() + " - " + option.getSortBy() + " " + option.getSortOrder()); +} +``` + # Testing Download the repository and run the following commands from `./constructorio-client` From 0d6cb2e1b217204dff7db4489e47db339d8a7e28 Mon Sep 17 00:00:00 2001 From: Enes Kutay SEZEN Date: Wed, 26 Nov 2025 22:00:03 +0100 Subject: [PATCH 04/10] Update test --- .../client/ConstructorIOSortOptionsTest.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java index f3e84847..8e17065b 100644 --- a/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java +++ b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java @@ -186,10 +186,13 @@ public void testDeleteSortOptionWithSortByAndSortOrder() throws Exception { constructor.createSortOption(new SortOptionRequest(sortOption, "Products")); // Delete the sort option + // DELETE endpoint returns 204 with no body String deleteResponse = constructor.deleteSortOptions("delete_test", "ascending", "Products"); - JSONObject jsonObj = new JSONObject(deleteResponse); - assertTrue("Response indicates success", jsonObj.has("message") || jsonObj.has("sort_by")); + // Verify that the response is empty (204 No Content) + assertTrue( + "DELETE should return empty response", + deleteResponse == null || deleteResponse.trim().isEmpty()); } @Test @@ -206,10 +209,13 @@ public void testDeleteSortOptionWithDefaultSection() throws Exception { constructor.createSortOption(new SortOptionRequest(sortOption, "Products")); // Delete the sort option using default section + // DELETE endpoint returns 204 with no body String deleteResponse = constructor.deleteSortOptions("delete_default", "descending"); - JSONObject jsonObj = new JSONObject(deleteResponse); - assertTrue("Response indicates success", jsonObj.has("message") || jsonObj.has("sort_by")); + // Verify that the response is empty (204 No Content) + assertTrue( + "DELETE should return empty response", + deleteResponse == null || deleteResponse.trim().isEmpty()); } @Test(expected = IllegalArgumentException.class) From 0003c35c06e8bdecc8433c47f97caedc0e1c81c2 Mon Sep 17 00:00:00 2001 From: Enes Kutay SEZEN Date: Thu, 4 Dec 2025 22:37:32 +0300 Subject: [PATCH 05/10] Newline at the end of file --- .../src/main/java/io/constructor/client/SortOptionRequest.java | 2 +- .../src/main/java/io/constructor/client/models/SortOption.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/constructorio-client/src/main/java/io/constructor/client/SortOptionRequest.java b/constructorio-client/src/main/java/io/constructor/client/SortOptionRequest.java index 0fbf1ded..233402ef 100644 --- a/constructorio-client/src/main/java/io/constructor/client/SortOptionRequest.java +++ b/constructorio-client/src/main/java/io/constructor/client/SortOptionRequest.java @@ -61,4 +61,4 @@ public String getSection() { public void setSection(String section) { this.section = section; } -} \ No newline at end of file +} diff --git a/constructorio-client/src/main/java/io/constructor/client/models/SortOption.java b/constructorio-client/src/main/java/io/constructor/client/models/SortOption.java index 646b4cbc..ac0ff038 100644 --- a/constructorio-client/src/main/java/io/constructor/client/models/SortOption.java +++ b/constructorio-client/src/main/java/io/constructor/client/models/SortOption.java @@ -106,4 +106,4 @@ public Boolean getHidden() { public void setHidden(Boolean hidden) { this.hidden = hidden; } -} \ No newline at end of file +} From f3e08b465fdee3d8521ece36b801391223316c73 Mon Sep 17 00:00:00 2001 From: Enes Kutay SEZEN Date: Thu, 4 Dec 2025 22:38:34 +0300 Subject: [PATCH 06/10] Remove gson --- .../constructor/client/ConstructorIOSortOptionsTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java index 8e17065b..50a3fbd4 100644 --- a/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java +++ b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java @@ -2,7 +2,6 @@ import static org.junit.Assert.*; -import com.google.gson.Gson; import io.constructor.client.models.SortOption; import io.constructor.client.models.SortOptionsResponse; import java.util.ArrayList; @@ -51,7 +50,8 @@ public static void cleanupSortOptions() throws ConstructorException { } } - @Rule public ExpectedException thrown = ExpectedException.none(); + @Rule + public ExpectedException thrown = ExpectedException.none(); @Test public void createSortOptionShouldReturn() throws Exception { @@ -267,8 +267,7 @@ public void testRetrieveSortOptionsWithFilter() throws Exception { addSortOptionToCleanupArray("retrieve_filter_test", "ascending"); // Retrieve with filter - SortOptionsResponse response = - constructor.retrieveSortOptions("Products", "retrieve_filter_test"); + SortOptionsResponse response = constructor.retrieveSortOptions("Products", "retrieve_filter_test"); assertNotNull(response); assertNotNull(response.getSortOptions()); From 362baa7a948408ecf449f6a09b3e0e4103ee11a1 Mon Sep 17 00:00:00 2001 From: Enes Kutay SEZEN Date: Thu, 4 Dec 2025 22:51:05 +0300 Subject: [PATCH 07/10] Lint --- .../client/ConstructorIOSortOptionsTest.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java index 50a3fbd4..8616c35a 100644 --- a/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java +++ b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java @@ -42,16 +42,12 @@ public static void cleanupSortOptions() throws ConstructorException { constructor.deleteSortOptions(sortBy, sortOrder, section); } catch (ConstructorException e) { System.err.println( - "Warning: Failed to clean up sort option: " - + sortBy - + " " - + sortOrder); + "Warning: Failed to clean up sort option: " + sortBy + " " + sortOrder); } } } - @Rule - public ExpectedException thrown = ExpectedException.none(); + @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void createSortOptionShouldReturn() throws Exception { @@ -187,7 +183,8 @@ public void testDeleteSortOptionWithSortByAndSortOrder() throws Exception { // Delete the sort option // DELETE endpoint returns 204 with no body - String deleteResponse = constructor.deleteSortOptions("delete_test", "ascending", "Products"); + String deleteResponse = + constructor.deleteSortOptions("delete_test", "ascending", "Products"); // Verify that the response is empty (204 No Content) assertTrue( @@ -267,7 +264,8 @@ public void testRetrieveSortOptionsWithFilter() throws Exception { addSortOptionToCleanupArray("retrieve_filter_test", "ascending"); // Retrieve with filter - SortOptionsResponse response = constructor.retrieveSortOptions("Products", "retrieve_filter_test"); + SortOptionsResponse response = + constructor.retrieveSortOptions("Products", "retrieve_filter_test"); assertNotNull(response); assertNotNull(response.getSortOptions()); From 00ce85a21b50d56ed214c78a8feeb1378ee9c217 Mon Sep 17 00:00:00 2001 From: mudaafi Date: Fri, 5 Dec 2025 07:01:19 +0800 Subject: [PATCH 08/10] update implementation --- README.md | 37 +++-- .../io/constructor/client/ConstructorIO.java | 133 ++++++++++++------ .../client/SortOptionGetRequest.java | 80 +++++++++++ .../constructor/client/models/SortOption.java | 37 ++++- .../client/ConstructorIOSortOptionsTest.java | 77 +++------- .../client/SortOptionRequestTest.java | 17 +-- .../io/constructor/client/SortOptionTest.java | 18 +-- .../client/SortOptionsResponseTest.java | 6 +- 8 files changed, 270 insertions(+), 135 deletions(-) create mode 100644 constructorio-client/src/main/java/io/constructor/client/SortOptionGetRequest.java diff --git a/README.md b/README.md index ed3e2f0d..cd2aaaf4 100644 --- a/README.md +++ b/README.md @@ -411,11 +411,9 @@ To create a new sort option, you will need to create a `SortOption` object with ```java // Create a SortOption with the required fields -SortOption sortOption = new SortOption(); +SortOption sortOption = new SortOption("price", SortOption.SortOrder.ascending); sortOption.setDisplayName("Price"); sortOption.setPathInMetadata("price_min"); -sortOption.setSortBy("price"); -sortOption.setSortOrder("ascending"); sortOption.setHidden(false); // Create a SortOptionRequest with the sort option and section @@ -431,11 +429,9 @@ To update an existing sort option (or create it if it doesn't exist), use the `u ```java // Create a SortOption with updated fields -SortOption sortOption = new SortOption(); +SortOption sortOption = new SortOption("price", SortOption.SortOrder.ascending); sortOption.setDisplayName("Price (Low to High)"); sortOption.setPathInMetadata("price_min"); -sortOption.setSortBy("price"); -sortOption.setSortOrder("ascending"); sortOption.setHidden(false); sortOption.setPosition(1); @@ -451,26 +447,39 @@ String response = constructor.updateSortOption(request); To delete a sort option, you need to specify the `sortBy` and `sortOrder` fields that identify it. ```java -// Delete a sort option by sortBy and sortOrder -String response = constructor.deleteSortOptions("price", "ascending", "Products"); +// Delete a single sort option by sortBy and sortOrder +String response = constructor.deleteSortOption("price", SortOption.SortOrder.ascending, "Products"); // Or use the default section "Products" -String response = constructor.deleteSortOptions("price", "ascending"); +String response = constructor.deleteSortOption("price", SortOption.SortOrder.ascending); + +// To delete multiple sort options at once +SortOption[] sortOptions = new SortOption[] { + new SortOption("price", SortOption.SortOrder.ascending), + new SortOption("relevance", SortOption.SortOrder.descending) +}; +String response = constructor.deleteSortOptions(sortOptions, "Products"); ``` ## Retrieving Sort Options -To retrieve all sort options for a section, you can use the `retrieveSortOptions` method. You can optionally filter by a specific `sortBy` field. +To retrieve all sort options for a section, you can use the `retrieveSortOptions` method. You can optionally filter by a specific `sortBy` field and control pagination. ```java // Retrieve all sort options for the default "Products" section SortOptionsResponse response = constructor.retrieveSortOptions(); -// Retrieve sort options for a specific section -SortOptionsResponse response = constructor.retrieveSortOptions("Products", null); - // Retrieve sort options filtered by sortBy field -SortOptionsResponse response = constructor.retrieveSortOptions("Products", "price"); +SortOptionsResponse response = constructor.retrieveSortOptions("price"); + +// Advanced retrieval with pagination and filters +SortOptionGetRequest request = new SortOptionGetRequest(); +request.setSection("Products"); +request.setSortBy("price"); +request.setPage(1); +request.setResultsPerPage(20); + +SortOptionsResponse response = constructor.retrieveSortOptions(request); // Access the results int totalCount = response.getTotalCount(); diff --git a/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java b/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java index 825ab2bf..a39a0feb 100644 --- a/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java +++ b/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java @@ -2,6 +2,7 @@ import com.google.gson.Gson; import io.constructor.client.models.*; +import io.constructor.client.models.SortOption.SortOrder; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -114,8 +115,8 @@ public static OkHttpClient getHttpClient() { /** * Creates a constructor.io Client. * - * @param apiToken API Token, gotten from your Constructor.io Dashboard, and kept secret. + * @param apiToken API Token, gotten from your Constructor.io Dashboard, and kept secret. * @param apiKey API Key, used publicly in your in-site javascript client. * @param isHTTPS true to use HTTPS, false to use HTTP. It is highly recommended that you use * HTTPS. @@ -147,8 +148,8 @@ public ConstructorIO( /** * Creates a constructor.io Client. * - * @param apiToken API Token, gotten from your Constructor.io Dashboard, and kept secret. + * @param apiToken API Token, gotten from your Constructor.io Dashboard, and kept secret. * @param apiKey API Key, used publicly in your in-site javascript client. * @param constructorToken The token provided by Constructor to identify your company's traffic * if proxying requests for results @@ -190,10 +191,19 @@ public ConstructorIO(String apiToken, String apiKey, boolean isHTTPS, String hos /* * Creates a constructor.io Client. * - * @param apiToken API Token, gotten from your Constructor.io Dashboard, and kept secret. + * @param apiToken API Token, gotten from your Constructor.io Dashboard, and + * kept secret. + * * @param apiKey API Key, used publicly in your in-site javascript client. - * @param isHTTPS true to use HTTPS, false to use HTTP. It is highly recommended that you use HTTPS. - * @param host The host of the autocomplete service that you are using. It is recommended that you let this value be null, in which case the host defaults to the Constructor.io autocomplete service at ac.cnstrc.com. + * + * @param isHTTPS true to use HTTPS, false to use HTTP. It is highly recommended + * that you use HTTPS. + * + * @param host The host of the autocomplete service that you are using. It is + * recommended that you let this value be null, in which case the host defaults + * to the Constructor.io autocomplete service at ac.cnstrc.com. + * * @param port The port to connect to */ public ConstructorIO(String apiToken, String apiKey, boolean isHTTPS, String host, int port) { @@ -2234,7 +2244,8 @@ protected static void moveMetadataOutOfResultData(JSONArray results) { JSONObject resultData = result.getJSONObject("data"); JSONObject metadata = new JSONObject(); - // Recursive call to move unspecified properties in result variations to its metadata + // Recursive call to move unspecified properties in result variations to its + // metadata // object if (!result.isNull("variations")) { JSONArray variations = result.getJSONArray("variations"); @@ -2295,7 +2306,8 @@ protected static JSONArray transformItemsAPIV2Response(JSONArray results) { // Add metadata to result data object result.put("metadata", metadata); - // Suggested score is already at the top level but its name needs to be converted to + // Suggested score is already at the top level but its name needs to be + // converted to // camelcase if (result.has("suggested_score")) { result.put("suggestedScore", result.get("suggested_score")); @@ -3146,15 +3158,6 @@ public String createSortOption(SortOptionRequest sortOptionRequest) */ public String updateSortOption(SortOptionRequest sortOptionRequest) throws ConstructorException { - if (sortOptionRequest.getSortOption().getSortBy() == null - || sortOptionRequest.getSortOption().getSortBy().trim().isEmpty()) { - throw new IllegalArgumentException("sortBy is required for update"); - } - if (sortOptionRequest.getSortOption().getSortOrder() == null - || sortOptionRequest.getSortOption().getSortOrder().trim().isEmpty()) { - throw new IllegalArgumentException("sortOrder is required for update"); - } - try { HttpUrl url = this.makeUrl( @@ -3162,7 +3165,7 @@ public String updateSortOption(SortOptionRequest sortOptionRequest) "v1", "sort_option", sortOptionRequest.getSortOption().getSortBy(), - sortOptionRequest.getSortOption().getSortOrder())); + sortOptionRequest.getSortOption().getSortOrder().toString())); url = url.newBuilder() .addQueryParameter("section", sortOptionRequest.getSection()) @@ -3199,19 +3202,15 @@ public String updateSortOption(SortOptionRequest sortOptionRequest) /** * Deletes sort options * - * @param sortBy the sort by field - * @param sortOrder the sort order (ascending or descending) - * @param section the section to which the sort option belongs + * @param sortOptions array of sortOptions to delete. Only the sortBy and sortOrder fields are required + * @param section the index section to delete the sort option from * @return returns the deleted sort options as JSON string * @throws ConstructorException if the request is invalid */ - public String deleteSortOptions(String sortBy, String sortOrder, String section) + public String deleteSortOptions(SortOption[] sortOptions, String section) throws ConstructorException { - if (sortBy == null || sortBy.trim().isEmpty()) { - throw new IllegalArgumentException("sortBy is required"); - } - if (sortOrder == null || sortOrder.trim().isEmpty()) { - throw new IllegalArgumentException("sortOrder is required"); + if (sortOptions == null || sortOptions.length < 1) { + throw new IllegalArgumentException("must specify sort options to detele"); } try { @@ -3221,12 +3220,8 @@ public String deleteSortOptions(String sortBy, String sortOrder, String section) .addQueryParameter("section", section) .build(); - Map sortOptionParams = new HashMap(); - sortOptionParams.put("sort_by", sortBy); - sortOptionParams.put("sort_order", sortOrder); - Map bodyParams = new HashMap(); - bodyParams.put("sort_options", Arrays.asList(sortOptionParams)); + bodyParams.put("sort_options", Arrays.asList(sortOptions)); String jsonParams = new Gson().toJson(bodyParams); RequestBody body = @@ -3245,33 +3240,86 @@ public String deleteSortOptions(String sortBy, String sortOrder, String section) /** * Deletes sort options with default section "Products" * + * @param sortOptions array of sortOptions to delete. Only the sortBy and sortOrder fields are required + * @return returns the deleted sort options as JSON string + * @throws ConstructorException if the request is invalid + */ + public String deleteSortOptions(SortOption[] sortOptions) throws ConstructorException { + return deleteSortOptions(sortOptions, "Products"); + } + + /** + * Deletes a single sort option + * + * @param sortBy the sort by field + * @param sortOrder the sort order (ascending or descending) + * @param section the index section to delete the sort option from + * @return returns the deleted sort options as JSON string + * @throws ConstructorException if the request is invalid + */ + public String deleteSortOption(String sortBy, SortOrder sortOrder, String section) + throws ConstructorException { + if (sortBy == null || sortBy.trim().isEmpty()) { + throw new IllegalArgumentException("sortBy is required"); + } + if (sortOrder == null) { + throw new IllegalArgumentException("sortOrder is required"); + } + + return deleteSortOptions(new SortOption[] {new SortOption(sortBy, sortOrder)}, section); + } + + /** + * Deletes a single sort option with default section "Products" + * * @param sortBy the sort by field * @param sortOrder the sort order (ascending or descending) * @return returns the deleted sort options as JSON string * @throws ConstructorException if the request is invalid */ - public String deleteSortOptions(String sortBy, String sortOrder) throws ConstructorException { - return deleteSortOptions(sortBy, sortOrder, "Products"); + public String deleteSortOption(String sortBy, SortOrder sortOrder) throws ConstructorException { + return deleteSortOption(sortBy, sortOrder, "Products"); } /** * Retrieves all sort options * - * @param section the section to retrieve sort options for - * @param sortBy optional filter by sort by field (can be null) + * @param sortOptionGetRequest the sort options get request * @return returns the sort options response * @throws ConstructorException if the request is invalid */ - public SortOptionsResponse retrieveSortOptions(String section, String sortBy) + public SortOptionsResponse retrieveSortOptions(SortOptionGetRequest sortOptionGetRequest) throws ConstructorException { + try { HttpUrl url = this.makeUrl(Arrays.asList("v1", "sort_options")); - HttpUrl.Builder urlBuilder = url.newBuilder().addQueryParameter("section", section); + HttpUrl.Builder urlBuilder = url.newBuilder(); + String sortBy = sortOptionGetRequest.getSortBy(); if (sortBy != null && !sortBy.trim().isEmpty()) { urlBuilder.addQueryParameter("sort_by", sortBy); } + String section = sortOptionGetRequest.getSection(); + if (section != null && !section.trim().isEmpty()) { + urlBuilder.addQueryParameter("section", section); + } + + Integer page = sortOptionGetRequest.getPage(); + if (page != null && page > 0) { + urlBuilder.addQueryParameter("page", page.toString()); + } + + Integer resultsPerPage = sortOptionGetRequest.getResultsPerPage(); + if (resultsPerPage != null && resultsPerPage > 0) { + urlBuilder.addQueryParameter("num_results_per_page", resultsPerPage.toString()); + } + + Integer offset = sortOptionGetRequest.getOffset(); + if (offset != null && offset > 0 && page == null) { + urlBuilder.addQueryParameter("offset", offset.toString()); + } + url = urlBuilder.build(); Request request = this.makeAuthorizedRequestBuilder().url(url).get().build(); @@ -3293,7 +3341,10 @@ public SortOptionsResponse retrieveSortOptions(String section, String sortBy) * @throws ConstructorException if the request is invalid */ public SortOptionsResponse retrieveSortOptions(String sortBy) throws ConstructorException { - return retrieveSortOptions("Products", sortBy); + SortOptionGetRequest getRequestParams = new SortOptionGetRequest(); + getRequestParams.setSortBy(sortBy); + + return retrieveSortOptions(getRequestParams); } /** @@ -3303,6 +3354,6 @@ public SortOptionsResponse retrieveSortOptions(String sortBy) throws Constructor * @throws ConstructorException if the request is invalid */ public SortOptionsResponse retrieveSortOptions() throws ConstructorException { - return retrieveSortOptions("Products", null); + return retrieveSortOptions(new SortOptionGetRequest()); } } diff --git a/constructorio-client/src/main/java/io/constructor/client/SortOptionGetRequest.java b/constructorio-client/src/main/java/io/constructor/client/SortOptionGetRequest.java new file mode 100644 index 00000000..8364eda9 --- /dev/null +++ b/constructorio-client/src/main/java/io/constructor/client/SortOptionGetRequest.java @@ -0,0 +1,80 @@ +package io.constructor.client; + +/** Constructor.io Sort Option Get Request */ +public class SortOptionGetRequest { + private String section; + private Integer page; + private Integer resultsPerPage; + private Integer offset; + private String sortBy; + + /** + * @param sortBy the optional filter by sortBy field (can be null) + */ + public void setSortBy(String sortBy) { + this.sortBy = sortBy; + } + + /** + * @return the optional filter by sortBy field (can be null) + */ + public String getSortBy() { + return sortBy; + } + + /** + * @param page the page of results to return + */ + public void setPage(int page) { + this.page = page; + } + + /** + * @return the page of results to return + */ + public Integer getPage() { + return page; + } + + /** + * @return the resultsPerPage + */ + public Integer getResultsPerPage() { + return resultsPerPage; + } + + /** + * @param resultsPerPage the resultsPerPage to set + */ + public void setResultsPerPage(int resultsPerPage) { + this.resultsPerPage = resultsPerPage; + } + + /** + * @param offset the offset of results to return (can't be used with page) + */ + public void setOffset(int offset) { + this.offset = offset; + } + + /** + * @return the offset of results to return + */ + public Integer getOffset() { + return offset; + } + + /** + * @return the section + */ + public String getSection() { + return section; + } + + /** + * @param section the section to set + */ + public void setSection(String section) { + this.section = section; + } +} diff --git a/constructorio-client/src/main/java/io/constructor/client/models/SortOption.java b/constructorio-client/src/main/java/io/constructor/client/models/SortOption.java index ac0ff038..34295cc5 100644 --- a/constructorio-client/src/main/java/io/constructor/client/models/SortOption.java +++ b/constructorio-client/src/main/java/io/constructor/client/models/SortOption.java @@ -5,6 +5,13 @@ /** Constructor.io Sort Option ... uses Gson/Reflection to load data in */ public class SortOption { + public enum SortOrder { + @SerializedName("ascending") + ascending, + @SerializedName("descending") + descending, + } + @SerializedName("display_name") private String displayName; @@ -12,7 +19,7 @@ public class SortOption { private String sortBy; @SerializedName("sort_order") - private String sortOrder; + private SortOrder sortOrder; @SerializedName("path_in_metadata") private String pathInMetadata; @@ -23,6 +30,22 @@ public class SortOption { @SerializedName("hidden") private Boolean hidden; + /** No-arg constructor for Gson deserialization only */ + private SortOption() {} + + public SortOption(String sortBy, SortOrder sortOrder) { + if (sortBy == null) { + throw new IllegalArgumentException("sortBy is required"); + } + + if (sortOrder == null) { + throw new IllegalArgumentException("sortOrder is required"); + } + + this.sortBy = sortBy; + this.sortOrder = sortOrder; + } + /** * @return the display name */ @@ -48,20 +71,28 @@ public String getSortBy() { * @param sortBy the sort by field to set */ public void setSortBy(String sortBy) { + if (sortBy == null) { + throw new IllegalArgumentException("sortBy is required"); + } + this.sortBy = sortBy; } /** * @return the sort order (ascending or descending) */ - public String getSortOrder() { + public SortOrder getSortOrder() { return sortOrder; } /** * @param sortOrder the sort order to set (ascending or descending) */ - public void setSortOrder(String sortOrder) { + public void setSortOrder(SortOrder sortOrder) { + if (sortOrder == null) { + throw new IllegalArgumentException("sortOrder is required"); + } + this.sortOrder = sortOrder; } diff --git a/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java index 8616c35a..25bac680 100644 --- a/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java +++ b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java @@ -3,6 +3,7 @@ import static org.junit.Assert.*; import io.constructor.client.models.SortOption; +import io.constructor.client.models.SortOption.SortOrder; import io.constructor.client.models.SortOptionsResponse; import java.util.ArrayList; import org.json.JSONObject; @@ -39,7 +40,7 @@ public static void cleanupSortOptions() throws ConstructorException { String section = parts[2]; try { - constructor.deleteSortOptions(sortBy, sortOrder, section); + constructor.deleteSortOption(sortBy, SortOrder.valueOf(sortOrder), section); } catch (ConstructorException e) { System.err.println( "Warning: Failed to clean up sort option: " + sortBy + " " + sortOrder); @@ -53,12 +54,10 @@ public static void cleanupSortOptions() throws ConstructorException { public void createSortOptionShouldReturn() throws Exception { ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); - SortOption sortOption = new SortOption(); + SortOption sortOption = new SortOption("test_price", SortOrder.ascending); sortOption.setDisplayName("Test Price Sort"); sortOption.setPathInMetadata("test_price"); sortOption.setHidden(false); - sortOption.setSortBy("test_price"); - sortOption.setSortOrder("ascending"); SortOptionRequest request = new SortOptionRequest(sortOption, "Products"); @@ -91,11 +90,9 @@ public void testCreateSortOptionWithNullSortOption() throws Exception { public void testCreateSortOptionWithDifferentSection() throws Exception { ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); - SortOption sortOption = new SortOption(); + SortOption sortOption = new SortOption("custom_field", SortOrder.descending); sortOption.setDisplayName("Custom Section Sort"); sortOption.setPathInMetadata("custom_field"); - sortOption.setSortBy("custom_field"); - sortOption.setSortOrder("descending"); sortOption.setHidden(true); SortOptionRequest request = new SortOptionRequest(sortOption, "Search Suggestions"); @@ -114,21 +111,17 @@ public void testUpdateSortOption() throws Exception { ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); // Create a sort option first - SortOption createOption = new SortOption(); + SortOption createOption = new SortOption("update_test", SortOrder.ascending); createOption.setDisplayName("Original Name"); createOption.setPathInMetadata("update_test"); - createOption.setSortBy("update_test"); - createOption.setSortOrder("ascending"); createOption.setHidden(false); constructor.createSortOption(new SortOptionRequest(createOption, "Products")); // Update the sort option - SortOption updateOption = new SortOption(); + SortOption updateOption = new SortOption("update_test", SortOrder.ascending); updateOption.setDisplayName("Updated Name"); updateOption.setPathInMetadata("update_test_v2"); - updateOption.setSortBy("update_test"); - updateOption.setSortOrder("ascending"); updateOption.setHidden(true); updateOption.setPosition(5); @@ -144,47 +137,21 @@ public void testUpdateSortOption() throws Exception { addSortOptionToCleanupArray("update_test", "ascending"); } - @Test(expected = IllegalArgumentException.class) - public void testUpdateSortOptionWithoutSortBy() throws Exception { - ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); - - SortOption sortOption = new SortOption(); - sortOption.setDisplayName("Test"); - sortOption.setSortOrder("ascending"); - - SortOptionRequest request = new SortOptionRequest(sortOption, "Products"); - constructor.updateSortOption(request); - } - - @Test(expected = IllegalArgumentException.class) - public void testUpdateSortOptionWithoutSortOrder() throws Exception { - ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); - - SortOption sortOption = new SortOption(); - sortOption.setDisplayName("Test"); - sortOption.setSortBy("test_field"); - - SortOptionRequest request = new SortOptionRequest(sortOption, "Products"); - constructor.updateSortOption(request); - } - @Test public void testDeleteSortOptionWithSortByAndSortOrder() throws Exception { ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); // Create a sort option first - SortOption sortOption = new SortOption(); + SortOption sortOption = new SortOption("delete_test", SortOrder.ascending); sortOption.setDisplayName("Delete Test"); sortOption.setPathInMetadata("delete_test"); - sortOption.setSortBy("delete_test"); - sortOption.setSortOrder("ascending"); constructor.createSortOption(new SortOptionRequest(sortOption, "Products")); // Delete the sort option // DELETE endpoint returns 204 with no body String deleteResponse = - constructor.deleteSortOptions("delete_test", "ascending", "Products"); + constructor.deleteSortOption("delete_test", SortOrder.ascending, "Products"); // Verify that the response is empty (204 No Content) assertTrue( @@ -197,17 +164,15 @@ public void testDeleteSortOptionWithDefaultSection() throws Exception { ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); // Create a sort option first - SortOption sortOption = new SortOption(); + SortOption sortOption = new SortOption("delete_default", SortOrder.descending); sortOption.setDisplayName("Delete Default Section"); sortOption.setPathInMetadata("delete_default"); - sortOption.setSortBy("delete_default"); - sortOption.setSortOrder("descending"); constructor.createSortOption(new SortOptionRequest(sortOption, "Products")); // Delete the sort option using default section // DELETE endpoint returns 204 with no body - String deleteResponse = constructor.deleteSortOptions("delete_default", "descending"); + String deleteResponse = constructor.deleteSortOption("delete_default", SortOrder.descending); // Verify that the response is empty (204 No Content) assertTrue( @@ -218,13 +183,13 @@ public void testDeleteSortOptionWithDefaultSection() throws Exception { @Test(expected = IllegalArgumentException.class) public void testDeleteSortOptionWithEmptySortBy() throws Exception { ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); - constructor.deleteSortOptions("", "ascending", "Products"); + constructor.deleteSortOption("", SortOrder.ascending, "Products"); } @Test(expected = IllegalArgumentException.class) - public void testDeleteSortOptionWithEmptySortOrder() throws Exception { + public void testDeleteSortOptionWithNullSortOrder() throws Exception { ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); - constructor.deleteSortOptions("test", "", "Products"); + constructor.deleteSortOption("test", null, "Products"); } @Test @@ -242,7 +207,10 @@ public void testRetrieveSortOptions() throws Exception { public void testRetrieveSortOptionsWithSection() throws Exception { ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); - SortOptionsResponse response = constructor.retrieveSortOptions("Products", null); + SortOptionGetRequest request = new SortOptionGetRequest(); + request.setSection("Products"); + + SortOptionsResponse response = constructor.retrieveSortOptions(request); assertNotNull(response); assertNotNull(response.getSortOptions()); @@ -254,18 +222,15 @@ public void testRetrieveSortOptionsWithFilter() throws Exception { ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); // Create a sort option to ensure there's something to retrieve - SortOption sortOption = new SortOption(); + SortOption sortOption = new SortOption("retrieve_filter_test", SortOrder.ascending); sortOption.setDisplayName("Retrieve Filter Test"); sortOption.setPathInMetadata("retrieve_filter_test"); - sortOption.setSortBy("retrieve_filter_test"); - sortOption.setSortOrder("ascending"); constructor.createSortOption(new SortOptionRequest(sortOption, "Products")); addSortOptionToCleanupArray("retrieve_filter_test", "ascending"); // Retrieve with filter - SortOptionsResponse response = - constructor.retrieveSortOptions("Products", "retrieve_filter_test"); + SortOptionsResponse response = constructor.retrieveSortOptions("retrieve_filter_test"); assertNotNull(response); assertNotNull(response.getSortOptions()); @@ -280,9 +245,11 @@ public void testRetrieveSortOptionsWithFilter() throws Exception { @Test public void testSortOptionDefaultValues() { - SortOption sortOption = new SortOption(); + SortOption sortOption = new SortOption("price", SortOrder.ascending); assertNull("Position should default to null", sortOption.getPosition()); assertNull("Hidden should default to null", sortOption.getHidden()); assertNull("Display name should default to null", sortOption.getDisplayName()); + assertEquals("Sort by should be set", "price", sortOption.getSortBy()); + assertEquals("Sort order should be set", SortOrder.ascending, sortOption.getSortOrder()); } } diff --git a/constructorio-client/src/test/java/io/constructor/client/SortOptionRequestTest.java b/constructorio-client/src/test/java/io/constructor/client/SortOptionRequestTest.java index 5eb9fca8..004873a0 100644 --- a/constructorio-client/src/test/java/io/constructor/client/SortOptionRequestTest.java +++ b/constructorio-client/src/test/java/io/constructor/client/SortOptionRequestTest.java @@ -4,6 +4,7 @@ import static org.junit.Assert.assertNotNull; import io.constructor.client.models.SortOption; +import io.constructor.client.models.SortOption.SortOrder; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -13,10 +14,8 @@ public class SortOptionRequestTest { @Test public void newSortOptionRequestShouldReturnWithDefaultSection() { - SortOption sortOption = new SortOption(); + SortOption sortOption = new SortOption("price", SortOrder.ascending); sortOption.setDisplayName("Price"); - sortOption.setSortBy("price"); - sortOption.setSortOrder("ascending"); SortOptionRequest request = new SortOptionRequest(sortOption); assertNotNull(request); @@ -26,10 +25,8 @@ public void newSortOptionRequestShouldReturnWithDefaultSection() { @Test public void newSortOptionRequestShouldReturnWithCustomSection() { - SortOption sortOption = new SortOption(); + SortOption sortOption = new SortOption("price", SortOrder.ascending); sortOption.setDisplayName("Price"); - sortOption.setSortBy("price"); - sortOption.setSortOrder("ascending"); SortOptionRequest request = new SortOptionRequest(sortOption, "Search Suggestions"); assertNotNull(request); @@ -39,13 +36,11 @@ public void newSortOptionRequestShouldReturnWithCustomSection() { @Test public void sortOptionRequestSettersWork() { - SortOption sortOption1 = new SortOption(); + SortOption sortOption1 = new SortOption("price", SortOrder.ascending); sortOption1.setDisplayName("Price"); - sortOption1.setSortBy("price"); - SortOption sortOption2 = new SortOption(); + SortOption sortOption2 = new SortOption("name", SortOrder.descending); sortOption2.setDisplayName("Name"); - sortOption2.setSortBy("name"); SortOptionRequest request = new SortOptionRequest(sortOption1); request.setSortOption(sortOption2); @@ -64,7 +59,7 @@ public void newSortOptionRequestShouldThrowExceptionWithNullSortOption() { @Test public void newSortOptionRequestShouldThrowExceptionWithNullSection() { - SortOption sortOption = new SortOption(); + SortOption sortOption = new SortOption("price", SortOrder.ascending); sortOption.setDisplayName("Price"); thrown.expect(IllegalArgumentException.class); diff --git a/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java b/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java index a0efa0f1..4b1bfeb2 100644 --- a/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java +++ b/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java @@ -2,9 +2,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import com.google.gson.Gson; import io.constructor.client.models.SortOption; +import io.constructor.client.models.SortOption.SortOrder; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -30,18 +34,16 @@ public void sortOptionDeserialization() throws Exception { assertNull(sortOption.getPosition()); assertEquals(sortOption.getHidden(), Boolean.TRUE); assertEquals(sortOption.getSortBy(), "price"); - assertEquals(sortOption.getSortOrder(), "ascending"); + assertEquals(sortOption.getSortOrder(), SortOrder.ascending); } @Test public void sortOptionSerialization() throws Exception { - SortOption sortOption = new SortOption(); + SortOption sortOption = new SortOption("price", SortOrder.descending); sortOption.setDisplayName("Preis"); sortOption.setPathInMetadata("price_min"); sortOption.setPosition(1); sortOption.setHidden(true); - sortOption.setSortBy("price"); - sortOption.setSortOrder("descending"); String json = new Gson().toJson(sortOption); @@ -52,18 +54,18 @@ public void sortOptionSerialization() throws Exception { assertEquals(deserialized.getPosition(), Integer.valueOf(1)); assertEquals(deserialized.getHidden(), Boolean.TRUE); assertEquals(deserialized.getSortBy(), "price"); - assertEquals(deserialized.getSortOrder(), "descending"); + assertEquals(deserialized.getSortOrder(), SortOrder.descending); } @Test public void sortOptionDefaultValues() { - SortOption sortOption = new SortOption(); + SortOption sortOption = new SortOption("price", SortOrder.ascending); assertNull("Display name should default to null", sortOption.getDisplayName()); assertNull("Path in metadata should default to null", sortOption.getPathInMetadata()); assertNull("Position should default to null", sortOption.getPosition()); assertNull("Hidden should default to null", sortOption.getHidden()); - assertNull("Sort by should default to null", sortOption.getSortBy()); - assertNull("Sort order should default to null", sortOption.getSortOrder()); + assertEquals("Sort by should be set", "price", sortOption.getSortBy()); + assertEquals("Sort order should be set", SortOrder.ascending, sortOption.getSortOrder()); } @Test diff --git a/constructorio-client/src/test/java/io/constructor/client/SortOptionsResponseTest.java b/constructorio-client/src/test/java/io/constructor/client/SortOptionsResponseTest.java index 7d91c23e..573278a5 100644 --- a/constructorio-client/src/test/java/io/constructor/client/SortOptionsResponseTest.java +++ b/constructorio-client/src/test/java/io/constructor/client/SortOptionsResponseTest.java @@ -52,7 +52,7 @@ public void sortOptionsResponseDeserialization() throws Exception { assertNull(first.getPosition()); assertEquals(first.getHidden(), Boolean.TRUE); assertEquals(first.getSortBy(), "price"); - assertEquals(first.getSortOrder(), "ascending"); + assertEquals(first.getSortOrder(), SortOption.SortOrder.ascending); // Check second sort option SortOption second = response.getSortOptions().get(1); @@ -61,7 +61,7 @@ public void sortOptionsResponseDeserialization() throws Exception { assertEquals(second.getPosition(), Integer.valueOf(1)); assertEquals(second.getHidden(), Boolean.FALSE); assertEquals(second.getSortBy(), "price"); - assertEquals(second.getSortOrder(), "descending"); + assertEquals(second.getSortOrder(), SortOption.SortOrder.descending); } @Test @@ -81,7 +81,7 @@ public void sortOptionsResponseSettersWork() { response.setTotalCount(5); java.util.ArrayList sortOptions = new java.util.ArrayList(); - SortOption sortOption = new SortOption(); + SortOption sortOption = new SortOption("price", SortOption.SortOrder.ascending); sortOption.setDisplayName("Test"); sortOptions.add(sortOption); From b60f115f03891351e70d70037270344a00ebe774 Mon Sep 17 00:00:00 2001 From: mudaafi Date: Fri, 5 Dec 2025 07:01:58 +0800 Subject: [PATCH 09/10] lint --- .../src/main/java/io/constructor/client/ConstructorIO.java | 6 ++++-- .../io/constructor/client/ConstructorIOSortOptionsTest.java | 3 ++- .../src/test/java/io/constructor/client/SortOptionTest.java | 3 --- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java b/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java index a39a0feb..d814ced9 100644 --- a/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java +++ b/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java @@ -3202,7 +3202,8 @@ public String updateSortOption(SortOptionRequest sortOptionRequest) /** * Deletes sort options * - * @param sortOptions array of sortOptions to delete. Only the sortBy and sortOrder fields are required + * @param sortOptions array of sortOptions to delete. Only the sortBy and sortOrder fields are + * required * @param section the index section to delete the sort option from * @return returns the deleted sort options as JSON string * @throws ConstructorException if the request is invalid @@ -3240,7 +3241,8 @@ public String deleteSortOptions(SortOption[] sortOptions, String section) /** * Deletes sort options with default section "Products" * - * @param sortOptions array of sortOptions to delete. Only the sortBy and sortOrder fields are required + * @param sortOptions array of sortOptions to delete. Only the sortBy and sortOrder fields are + * required * @return returns the deleted sort options as JSON string * @throws ConstructorException if the request is invalid */ diff --git a/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java index 25bac680..733f836c 100644 --- a/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java +++ b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSortOptionsTest.java @@ -172,7 +172,8 @@ public void testDeleteSortOptionWithDefaultSection() throws Exception { // Delete the sort option using default section // DELETE endpoint returns 204 with no body - String deleteResponse = constructor.deleteSortOption("delete_default", SortOrder.descending); + String deleteResponse = + constructor.deleteSortOption("delete_default", SortOrder.descending); // Verify that the response is empty (204 No Content) assertTrue( diff --git a/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java b/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java index 4b1bfeb2..599d07b4 100644 --- a/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java +++ b/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java @@ -2,13 +2,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import com.google.gson.Gson; import io.constructor.client.models.SortOption; import io.constructor.client.models.SortOption.SortOrder; -import java.lang.reflect.Constructor; -import java.lang.reflect.Modifier; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; From f47a07930d6f44308aa203c0b1fb0112c68e08f7 Mon Sep 17 00:00:00 2001 From: mudaafi Date: Fri, 5 Dec 2025 13:30:01 +0800 Subject: [PATCH 10/10] lint --- .../main/java/io/constructor/client/ConstructorIO.java | 2 +- .../test/java/io/constructor/client/SortOptionTest.java | 8 ++++---- .../io/constructor/client/SortOptionsResponseTest.java | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java b/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java index d814ced9..8b6aec5a 100644 --- a/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java +++ b/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java @@ -3336,7 +3336,7 @@ public SortOptionsResponse retrieveSortOptions(SortOptionGetRequest sortOptionGe } /** - * Retrieves all sort options with default section "Products" + * Retrieves all sort options for a specific sortBy with default section "Products" * * @param sortBy optional filter by sort by field (can be null) * @return returns the sort options response diff --git a/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java b/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java index 599d07b4..94f8be93 100644 --- a/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java +++ b/constructorio-client/src/test/java/io/constructor/client/SortOptionTest.java @@ -17,7 +17,7 @@ public class SortOptionTest { public void sortOptionDeserialization() throws Exception { String json = "{" - + "\"display_name\": \"Preis\"," + + "\"display_name\": \"Price\"," + "\"path_in_metadata\": \"price_min\"," + "\"position\": null," + "\"hidden\": true," @@ -26,7 +26,7 @@ public void sortOptionDeserialization() throws Exception { + "}"; SortOption sortOption = new Gson().fromJson(json, SortOption.class); - assertEquals(sortOption.getDisplayName(), "Preis"); + assertEquals(sortOption.getDisplayName(), "Price"); assertEquals(sortOption.getPathInMetadata(), "price_min"); assertNull(sortOption.getPosition()); assertEquals(sortOption.getHidden(), Boolean.TRUE); @@ -37,7 +37,7 @@ public void sortOptionDeserialization() throws Exception { @Test public void sortOptionSerialization() throws Exception { SortOption sortOption = new SortOption("price", SortOrder.descending); - sortOption.setDisplayName("Preis"); + sortOption.setDisplayName("Price"); sortOption.setPathInMetadata("price_min"); sortOption.setPosition(1); sortOption.setHidden(true); @@ -46,7 +46,7 @@ public void sortOptionSerialization() throws Exception { // Verify the JSON contains the expected fields SortOption deserialized = new Gson().fromJson(json, SortOption.class); - assertEquals(deserialized.getDisplayName(), "Preis"); + assertEquals(deserialized.getDisplayName(), "Price"); assertEquals(deserialized.getPathInMetadata(), "price_min"); assertEquals(deserialized.getPosition(), Integer.valueOf(1)); assertEquals(deserialized.getHidden(), Boolean.TRUE); diff --git a/constructorio-client/src/test/java/io/constructor/client/SortOptionsResponseTest.java b/constructorio-client/src/test/java/io/constructor/client/SortOptionsResponseTest.java index 573278a5..5c6ea684 100644 --- a/constructorio-client/src/test/java/io/constructor/client/SortOptionsResponseTest.java +++ b/constructorio-client/src/test/java/io/constructor/client/SortOptionsResponseTest.java @@ -21,7 +21,7 @@ public void sortOptionsResponseDeserialization() throws Exception { + "\"total_count\": 2," + "\"sort_options\": [" + " {" - + " \"display_name\": \"Preis\"," + + " \"display_name\": \"Price\"," + " \"path_in_metadata\": \"price_min\"," + " \"position\": null," + " \"hidden\": true," @@ -29,7 +29,7 @@ public void sortOptionsResponseDeserialization() throws Exception { + " \"sort_order\": \"ascending\"" + " }," + " {" - + " \"display_name\": \"Preis\"," + + " \"display_name\": \"Price\"," + " \"path_in_metadata\": \"price_min\"," + " \"position\": 1," + " \"hidden\": false," @@ -47,7 +47,7 @@ public void sortOptionsResponseDeserialization() throws Exception { // Check first sort option SortOption first = response.getSortOptions().get(0); - assertEquals(first.getDisplayName(), "Preis"); + assertEquals(first.getDisplayName(), "Price"); assertEquals(first.getPathInMetadata(), "price_min"); assertNull(first.getPosition()); assertEquals(first.getHidden(), Boolean.TRUE); @@ -56,7 +56,7 @@ public void sortOptionsResponseDeserialization() throws Exception { // Check second sort option SortOption second = response.getSortOptions().get(1); - assertEquals(second.getDisplayName(), "Preis"); + assertEquals(second.getDisplayName(), "Price"); assertEquals(second.getPathInMetadata(), "price_min"); assertEquals(second.getPosition(), Integer.valueOf(1)); assertEquals(second.getHidden(), Boolean.FALSE);