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 43d1ec2a..5e072e8c 100644 --- a/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java +++ b/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java @@ -2954,4 +2954,106 @@ public String deleteFacetConfiguration(FacetConfigurationRequest facetConfigurat facetConfigurationRequest.geFacetConfiguration().getName(), facetConfigurationRequest.getSection()); } + + /** + * Creates a facet option configuration + * + * @param request The facet option configuration request containing the configuration to create + * @return returns the created facet option configuration + * @throws ConstructorException if the request is invalid + */ + public String createFacetOptionConfiguration( + FacetOptionConfigurationRequest facetOptionConfigurationRequest) + throws ConstructorException { + try { + HttpUrl url = + this.makeUrl( + Arrays.asList( + "v1", + "facets", + facetOptionConfigurationRequest.getFacetName(), + "options")); + url = + url.newBuilder() + .addQueryParameter( + "section", facetOptionConfigurationRequest.getSection()) + .build(); + + String params = + new Gson() + .toJson(facetOptionConfigurationRequest.getFacetOptionConfiguration()); + 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); + } + } + + /** + * Deletes a facet option configuration + * + * @param facetName the name of the facet + * @param facetOptionValue the value of the facet option to delete + * @param section the section of the facet + * @return returns the deleted facet option configuration + * @throws ConstructorException if the request is invalid + */ + public String deleteFacetOptionConfiguration( + String facetName, String facetOptionValue, String section) throws ConstructorException { + if (facetName == null || facetName.trim().isEmpty()) { + throw new IllegalArgumentException("facetName is required"); + } + if (facetOptionValue == null || facetOptionValue.trim().isEmpty()) { + throw new IllegalArgumentException("facetOptionValue is required"); + } + + try { + HttpUrl url = + this.makeUrl( + Arrays.asList("v1", "facets", facetName, "options", facetOptionValue)); + url = url.newBuilder().addQueryParameter("section", section).build(); + + Request request = this.makeAuthorizedRequestBuilder().url(url).delete().build(); + + Response response = client.newCall(request).execute(); + + return getResponseBody(response); + } catch (Exception exception) { + throw new ConstructorException(exception); + } + } + + /** + * Deletes a facet option configuration with default section "Products" + * + * @param facetName the name of the facet + * @param facetOptionValue the value of the facet option to delete + * @return returns the deleted facet option configuration + * @throws ConstructorException if the request is invalid + */ + public String deleteFacetOptionConfiguration(String facetName, String facetOptionValue) + throws ConstructorException { + return deleteFacetOptionConfiguration(facetName, facetOptionValue, "Products"); + } + + /** + * Deletes a facet option configuration + * + * @param request The facet option configuration request containing the configuration to delete + * @return returns the deleted facet option configuration + * @throws ConstructorException if the request is invalid + */ + public String deleteFacetOptionConfiguration( + FacetOptionConfigurationRequest facetOptionConfigurationRequest) + throws ConstructorException { + return deleteFacetOptionConfiguration( + facetOptionConfigurationRequest.getFacetName(), + facetOptionConfigurationRequest.getFacetOptionConfiguration().getValue(), + facetOptionConfigurationRequest.getSection()); + } } diff --git a/constructorio-client/src/main/java/io/constructor/client/FacetOptionConfigurationRequest.java b/constructorio-client/src/main/java/io/constructor/client/FacetOptionConfigurationRequest.java new file mode 100644 index 00000000..e51741d4 --- /dev/null +++ b/constructorio-client/src/main/java/io/constructor/client/FacetOptionConfigurationRequest.java @@ -0,0 +1,84 @@ +package io.constructor.client; + +import io.constructor.client.models.FacetOptionConfiguration; + +/** Constructor.io Facet Option Configuration Request */ +public class FacetOptionConfigurationRequest { + private FacetOptionConfiguration facetOptionConfiguration; + private String facetName; + private String section; + + /** + * Creates a facet option configuration request + * + * @param facetOptionConfiguration the facet option configuration to be created + * @param facetName the name of the facet + * @param section the section to which the facet belongs + */ + public FacetOptionConfigurationRequest( + FacetOptionConfiguration facetOptionConfiguration, String facetName, String section) { + if (facetOptionConfiguration == null) { + throw new IllegalArgumentException("facetOptionConfiguration is required"); + } + if (facetName == null || facetName.trim().isEmpty()) { + throw new IllegalArgumentException("facetName is required"); + } + + this.facetOptionConfiguration = facetOptionConfiguration; + this.facetName = facetName; + this.section = section; + } + + /** + * Creates a facet option configuration request with default section "Products" + * + * @param option the facet option configuration to be created + * @param facetName the name of the facet + */ + public FacetOptionConfigurationRequest( + FacetOptionConfiguration facetOptionConfiguration, String facetName) { + this(facetOptionConfiguration, facetName, "Products"); + } + + /** + * @return the facet option configuration + */ + public FacetOptionConfiguration getFacetOptionConfiguration() { + return facetOptionConfiguration; + } + + /** + * @param option the facet option configuration to set + */ + public void setFacetOptionConfiguration(FacetOptionConfiguration facetOptionConfiguration) { + this.facetOptionConfiguration = facetOptionConfiguration; + } + + /** + * @return the facet name + */ + public String getFacetName() { + return facetName; + } + + /** + * @param facetName the facet name to set + */ + public void setFacetName(String facetName) { + this.facetName = facetName; + } + + /** + * @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/test/java/io/constructor/client/ConstructorIOFacetOptionConfigurationTest.java b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOFacetOptionConfigurationTest.java new file mode 100644 index 00000000..93fc6e4e --- /dev/null +++ b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOFacetOptionConfigurationTest.java @@ -0,0 +1,228 @@ +package io.constructor.client; + +import static org.junit.Assert.*; + +import com.google.gson.Gson; +import io.constructor.client.models.FacetConfiguration; +import io.constructor.client.models.FacetOptionConfiguration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import org.json.JSONObject; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class ConstructorIOFacetOptionConfigurationTest { + private static final String PRODUCTS_SECTION = "Products"; + private static final String SEARCH_SUGGESTIONS_SECTION = "Search Suggestions"; + private static String token = System.getenv("TEST_API_TOKEN"); + private static String apiKey = System.getenv("TEST_CATALOG_API_KEY"); + private static ArrayList facetOptionsToCleanup = new ArrayList<>(); + private static ArrayList facetsToCleanup = new ArrayList<>(); + private ConstructorIO constructor; + private FacetConfiguration baseFacetConfig; + + private static class FacetOptionToCleanup { + String facetName; + String optionValue; + String section; + + FacetOptionToCleanup(String facetName, String optionValue, String section) { + this.facetName = facetName; + this.optionValue = optionValue; + this.section = section; + } + } + + @Before + public void setUp() throws Exception { + constructor = new ConstructorIO(token, apiKey, true, null); + baseFacetConfig = + new Gson() + .fromJson( + Utils.getTestResource("facet.configuration.json"), + FacetConfiguration.class); + } + + private void addFacetOptionToCleanupArray( + String facetName, String optionValue, String section) { + facetOptionsToCleanup.add(new FacetOptionToCleanup(facetName, optionValue, section)); + } + + private void addFacetOptionToCleanupArray(String facetName, String optionValue) { + addFacetOptionToCleanupArray(facetName, optionValue, PRODUCTS_SECTION); + } + + private void addFacetToCleanupArray(String facetName, String section) { + facetsToCleanup.add(facetName + "|" + (section != null ? section : PRODUCTS_SECTION)); + } + + private void addFacetToCleanupArray(String facetName) { + addFacetToCleanupArray(facetName, PRODUCTS_SECTION); + } + + private FacetConfiguration createFacetConfigurationObject(String name, String section) { + FacetConfiguration config = new FacetConfiguration(); + config.setName(name); + config.setDisplayName(baseFacetConfig.getDisplayName()); + config.setType(baseFacetConfig.getType()); + return config; + } + + private FacetOptionConfiguration createFacetOptionConfigurationObject( + String value, String displayName, int position) { + FacetOptionConfiguration config = new FacetOptionConfiguration(); + config.setValue(value); + config.setDisplayName(displayName); + config.setPosition(position); + return config; + } + + @AfterClass + public static void cleanupFacetsWithOptions() throws ConstructorException { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + for (FacetOptionToCleanup facetOption : facetOptionsToCleanup) { + try { + constructor.deleteFacetOptionConfiguration( + facetOption.facetName, facetOption.optionValue, facetOption.section); + } catch (ConstructorException e) { + System.err.println( + "Warning: Failed to clean up facet option: " + facetOption.facetName); + } + } + + for (String facet : facetsToCleanup) { + String[] parts = facet.split("\\|"); + String facetName = parts[0]; + String section = parts[1]; + + try { + constructor.deleteFacetConfiguration(facetName, section); + } catch (ConstructorException e) { + System.err.println("Warning: Failed to clean up facet: " + facetName); + } + } + } + + @Rule public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testCreateFacetOptionConfiguration() throws Exception { + String facetName = "testFacet"; + constructor.createFacetConfiguration( + new FacetConfigurationRequest( + createFacetConfigurationObject(facetName, PRODUCTS_SECTION), + PRODUCTS_SECTION)); + addFacetToCleanupArray(facetName); + + // Create facet option configuration + FacetOptionConfiguration option = + createFacetOptionConfigurationObject("test-option", "Test Option", 1); + option.setValueAlias("test-alias"); + Map data = new HashMap<>(); + data.put("foo", "bar"); + option.setData(data); + option.setHidden(false); + + // Create and verify configuration + String response = + constructor.createFacetOptionConfiguration( + new FacetOptionConfigurationRequest(option, "testFacet", PRODUCTS_SECTION)); + JSONObject jsonObj = new JSONObject(response); + + assertEquals("test-option", jsonObj.get("value")); + assertEquals("test-alias", jsonObj.get("value_alias")); + assertEquals("Test Option", jsonObj.get("display_name")); + assertEquals(1, jsonObj.get("position")); + assertEquals(false, jsonObj.get("hidden")); + assertEquals("bar", jsonObj.getJSONObject("data").get("foo")); + + addFacetOptionToCleanupArray(facetName, "test-option"); + } + + @Test(expected = ConstructorException.class) + public void testCreateFacetOptionConfigurationWithNullRequest() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + constructor.createFacetOptionConfiguration(null); + } + + @Test + public void testCreateFacetOptionConfigurationWithDifferentSection() throws Exception { + String facetName = "testFacet_SearchSuggestions"; + String optionValue = "test-option-different-section"; + + constructor.createFacetConfiguration( + new FacetConfigurationRequest( + createFacetConfigurationObject(facetName, SEARCH_SUGGESTIONS_SECTION), + SEARCH_SUGGESTIONS_SECTION)); + addFacetToCleanupArray(facetName, SEARCH_SUGGESTIONS_SECTION); + + FacetOptionConfiguration option = + createFacetOptionConfigurationObject( + optionValue, "Test Option Different Section", 1); + String response = + constructor.createFacetOptionConfiguration( + new FacetOptionConfigurationRequest( + option, facetName, SEARCH_SUGGESTIONS_SECTION)); + + assertEquals(optionValue, new JSONObject(response).getString("value")); + addFacetOptionToCleanupArray(facetName, optionValue, SEARCH_SUGGESTIONS_SECTION); + } + + @Test + public void testDeleteFacetOptionConfigurationWithName() throws Exception { + String facetName = "testDeleteFacet"; + String optionValue = "test-option-to-delete"; + + constructor.createFacetConfiguration( + new FacetConfigurationRequest( + createFacetConfigurationObject(facetName, PRODUCTS_SECTION), + PRODUCTS_SECTION)); + addFacetToCleanupArray(facetName); + + FacetOptionConfiguration option = + createFacetOptionConfigurationObject(optionValue, "Test Option To Delete", 1); + constructor.createFacetOptionConfiguration( + new FacetOptionConfigurationRequest(option, facetName, PRODUCTS_SECTION)); + + String response = + constructor.deleteFacetOptionConfiguration( + facetName, optionValue, PRODUCTS_SECTION); + assertEquals(optionValue, new JSONObject(response).getString("value")); + addFacetOptionToCleanupArray(facetName, optionValue); + } + + @Test + public void testDeleteFacetOptionConfigurationWithRequest() throws Exception { + String facetName = "testDeleteWithRequestFacet"; + String optionValue = "test-delete-with-request"; + + constructor.createFacetConfiguration( + new FacetConfigurationRequest( + createFacetConfigurationObject(facetName, PRODUCTS_SECTION), + PRODUCTS_SECTION)); + addFacetToCleanupArray(facetName); + + FacetOptionConfigurationRequest request = + new FacetOptionConfigurationRequest( + createFacetOptionConfigurationObject( + optionValue, "Test Delete With Request", 1), + facetName, + PRODUCTS_SECTION); + constructor.createFacetOptionConfiguration(request); + + String response = constructor.deleteFacetOptionConfiguration(request); + assertEquals(optionValue, new JSONObject(response).getString("value")); + addFacetOptionToCleanupArray(facetName, optionValue); + } + + @Test(expected = ConstructorException.class) + public void testDeleteNonExistentFacetOptionShouldThrowException() throws Exception { + constructor.deleteFacetOptionConfiguration( + "nonExistentFacet", "nonExistentOption", PRODUCTS_SECTION); + } +}