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 210a5c19..88c715b0 100644 --- a/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java +++ b/constructorio-client/src/main/java/io/constructor/client/ConstructorIO.java @@ -3358,4 +3358,706 @@ public SortOptionsResponse retrieveSortOptions(String sortBy) throws Constructor public SortOptionsResponse retrieveSortOptions() throws ConstructorException { return retrieveSortOptions(new SortOptionGetRequest()); } + + // ==================== Facet Configuration V2 API ==================== + + /** Default section used when no section is specified */ + public static final String DEFAULT_SECTION = "Products"; + + /** + * Retrieves all facet configurations (v2) + * + * @param section the section to retrieve facets from + * @param page the page number (optional) + * @param numResultsPerPage the number of results per page (optional) + * @param offset the offset for pagination (optional, ignored if page is set) + * @return returns the facet configurations as JSON string + * @throws ConstructorException if the request is invalid + */ + public String retrieveFacetConfigurationsV2( + String section, Integer page, Integer numResultsPerPage, Integer offset) + throws ConstructorException { + try { + HttpUrl.Builder urlBuilder = this.makeUrl(Arrays.asList("v2", "facets")).newBuilder(); + + urlBuilder.addQueryParameter( + "section", + (section != null && !section.trim().isEmpty()) ? section : DEFAULT_SECTION); + + if (page != null && page > 0) { + urlBuilder.addQueryParameter("page", page.toString()); + } + if (numResultsPerPage != null && numResultsPerPage > 0) { + urlBuilder.addQueryParameter("num_results_per_page", numResultsPerPage.toString()); + } + if (offset != null && offset > 0 && page == null) { + urlBuilder.addQueryParameter("offset", offset.toString()); + } + + HttpUrl url = urlBuilder.build(); + Request request = this.makeAuthorizedRequestBuilder().url(url).get().build(); + + Response response = clientWithRetry.newCall(request).execute(); + + return getResponseBody(response); + } catch (Exception exception) { + throw new ConstructorException(exception); + } + } + + /** + * Retrieves all facet configurations (v2) + * + * @param section the section to retrieve facets from + * @param page the page number (optional) + * @param numResultsPerPage the number of results per page (optional) + * @return returns the facet configurations as JSON string + * @throws ConstructorException if the request is invalid + */ + public String retrieveFacetConfigurationsV2( + String section, Integer page, Integer numResultsPerPage) throws ConstructorException { + return retrieveFacetConfigurationsV2(section, page, numResultsPerPage, null); + } + + /** + * Retrieves all facet configurations (v2) with default section "Products" + * + * @return returns the facet configurations as JSON string + * @throws ConstructorException if the request is invalid + */ + public String retrieveFacetConfigurationsV2() throws ConstructorException { + return retrieveFacetConfigurationsV2(DEFAULT_SECTION, null, null, null); + } + + /** + * Retrieves a single facet configuration by name (v2) + * + * @param facetName the name of the facet to retrieve + * @param section the section to which the facet belongs + * @return returns the facet configuration as JSON string + * @throws IllegalArgumentException if facetName is null or empty + * @throws ConstructorException if the request fails + */ + public String retrieveFacetConfigurationV2(String facetName, String section) + throws ConstructorException { + if (facetName == null || facetName.trim().isEmpty()) { + throw new IllegalArgumentException("facetName is required"); + } + + try { + HttpUrl url = + this.makeUrl(Arrays.asList("v2", "facets", facetName)) + .newBuilder() + .addQueryParameter( + "section", section != null ? section : DEFAULT_SECTION) + .build(); + + Request request = this.makeAuthorizedRequestBuilder().url(url).get().build(); + + Response response = clientWithRetry.newCall(request).execute(); + + return getResponseBody(response); + } catch (Exception exception) { + throw new ConstructorException(exception); + } + } + + /** + * Retrieves a single facet configuration by name (v2) with default section "Products" + * + * @param facetName the name of the facet to retrieve + * @return returns the facet configuration as JSON string + * @throws IllegalArgumentException if facetName is null or empty + * @throws ConstructorException if the request fails + */ + public String retrieveFacetConfigurationV2(String facetName) throws ConstructorException { + return retrieveFacetConfigurationV2(facetName, DEFAULT_SECTION); + } + + /** + * Creates a facet configuration (v2) + * + * @param facetConfigurationV2Request the facet configuration v2 request + * @return returns the created facet as JSON string + * @throws IllegalArgumentException if request is null + * @throws ConstructorException if the request fails + */ + public String createFacetConfigurationV2( + FacetConfigurationV2Request facetConfigurationV2Request) throws ConstructorException { + if (facetConfigurationV2Request == null) { + throw new IllegalArgumentException("facetConfigurationV2Request is required"); + } + + try { + HttpUrl url = this.makeUrl(Arrays.asList("v2", "facets")); + url = + url.newBuilder() + .addQueryParameter("section", facetConfigurationV2Request.getSection()) + .build(); + + String params = new Gson().toJson(facetConfigurationV2Request.getFacetConfiguration()); + 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); + } + } + + /** + * Replaces a facet configuration (v2) + * + * @param facetConfigurationV2Request the facet configuration v2 request + * @return returns the replaced facet as JSON string + * @throws IllegalArgumentException if request is null or facetName is missing + * @throws ConstructorException if the request fails + */ + public String replaceFacetConfigurationV2( + FacetConfigurationV2Request facetConfigurationV2Request) throws ConstructorException { + if (facetConfigurationV2Request == null) { + throw new IllegalArgumentException("facetConfigurationV2Request is required"); + } + + String facetName = facetConfigurationV2Request.getFacetConfiguration().getName(); + if (facetName == null || facetName.trim().isEmpty()) { + throw new IllegalArgumentException("facetName is required"); + } + + try { + HttpUrl url = + this.makeUrl(Arrays.asList("v2", "facets", facetName)) + .newBuilder() + .addQueryParameter("section", facetConfigurationV2Request.getSection()) + .build(); + + String params = new Gson().toJson(facetConfigurationV2Request.getFacetConfiguration()); + 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); + } + } + + /** + * Updates a facet configuration (v2) - partial update + * + * @param facetConfigurationV2Request the facet configuration v2 request + * @return returns the updated facet as JSON string + * @throws IllegalArgumentException if request is null or facetName is missing + * @throws ConstructorException if the request fails + */ + public String updateFacetConfigurationV2( + FacetConfigurationV2Request facetConfigurationV2Request) throws ConstructorException { + if (facetConfigurationV2Request == null) { + throw new IllegalArgumentException("facetConfigurationV2Request is required"); + } + + String facetName = facetConfigurationV2Request.getFacetConfiguration().getName(); + if (facetName == null || facetName.trim().isEmpty()) { + throw new IllegalArgumentException("facetName is required"); + } + + try { + HttpUrl url = + this.makeUrl(Arrays.asList("v2", "facets", facetName)) + .newBuilder() + .addQueryParameter("section", facetConfigurationV2Request.getSection()) + .build(); + + String params = new Gson().toJson(facetConfigurationV2Request.getFacetConfiguration()); + RequestBody body = + RequestBody.create(params, MediaType.parse("application/json; charset=utf-8")); + Request request = this.makeAuthorizedRequestBuilder().url(url).patch(body).build(); + + Response response = client.newCall(request).execute(); + + return getResponseBody(response); + } catch (Exception exception) { + throw new ConstructorException(exception); + } + } + + /** + * Updates multiple facet configurations (v2) - bulk partial update + * + * @param facetConfigurationsV2Request the facet configurations v2 request + * @return returns the updated facets as JSON string + * @throws IllegalArgumentException if request is null + * @throws ConstructorException if the request fails + */ + public String updateFacetConfigurationsV2( + FacetConfigurationsV2Request facetConfigurationsV2Request) throws ConstructorException { + if (facetConfigurationsV2Request == null) { + throw new IllegalArgumentException("facetConfigurationsV2Request is required"); + } + + try { + HttpUrl url = + this.makeUrl(Arrays.asList("v2", "facets")) + .newBuilder() + .addQueryParameter("section", facetConfigurationsV2Request.getSection()) + .build(); + + Map bodyMap = new HashMap<>(); + bodyMap.put("facets", facetConfigurationsV2Request.getFacetConfigurations()); + String params = new Gson().toJson(bodyMap); + RequestBody body = + RequestBody.create(params, MediaType.parse("application/json; charset=utf-8")); + Request request = this.makeAuthorizedRequestBuilder().url(url).patch(body).build(); + + Response response = client.newCall(request).execute(); + + return getResponseBody(response); + } catch (Exception exception) { + throw new ConstructorException(exception); + } + } + + /** + * Replaces multiple facet configurations (v2) - bulk replace + * + * @param facetConfigurationsV2Request the facet configurations v2 request + * @return returns the replaced facets as JSON string + * @throws IllegalArgumentException if request is null + * @throws ConstructorException if the request fails + */ + public String replaceFacetConfigurationsV2( + FacetConfigurationsV2Request facetConfigurationsV2Request) throws ConstructorException { + if (facetConfigurationsV2Request == null) { + throw new IllegalArgumentException("facetConfigurationsV2Request is required"); + } + + try { + HttpUrl url = + this.makeUrl(Arrays.asList("v2", "facets")) + .newBuilder() + .addQueryParameter("section", facetConfigurationsV2Request.getSection()) + .build(); + + Map bodyMap = new HashMap<>(); + bodyMap.put("facets", facetConfigurationsV2Request.getFacetConfigurations()); + String params = new Gson().toJson(bodyMap); + 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 a facet configuration (v2) + * + * @param facetName the facet name + * @param section the section to which the facet belongs + * @return returns the deleted facet as JSON string + * @throws IllegalArgumentException if facetName is null or empty + * @throws ConstructorException if the request fails + */ + public String deleteFacetConfigurationV2(String facetName, String section) + throws ConstructorException { + if (facetName == null || facetName.trim().isEmpty()) { + throw new IllegalArgumentException("facetName is required"); + } + + try { + HttpUrl url = + this.makeUrl(Arrays.asList("v2", "facets", facetName)) + .newBuilder() + .addQueryParameter( + "section", section != null ? section : DEFAULT_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 configuration (v2) with default section "Products" + * + * @param facetName the facet name + * @return returns the deleted facet as JSON string + * @throws IllegalArgumentException if facetName is null or empty + * @throws ConstructorException if the request fails + */ + public String deleteFacetConfigurationV2(String facetName) throws ConstructorException { + return deleteFacetConfigurationV2(facetName, DEFAULT_SECTION); + } + + /** + * Deletes a facet configuration (v2) using a request object + * + * @param facetConfigurationV2Request the facetConfiguration v2 request + * @return returns the deleted facet as JSON string + * @throws IllegalArgumentException if request is null + * @throws ConstructorException if the request fails + */ + public String deleteFacetConfigurationV2( + FacetConfigurationV2Request facetConfigurationV2Request) throws ConstructorException { + if (facetConfigurationV2Request == null) { + throw new IllegalArgumentException("facetConfigurationV2Request is required"); + } + + return deleteFacetConfigurationV2( + facetConfigurationV2Request.getFacetConfiguration().getName(), + facetConfigurationV2Request.getSection()); + } + + // ==================== Searchability V2 API ==================== + + /** + * Retrieves all searchabilities (v2) + * + * @param request the searchabilities v2 GET request with filters and pagination + * @return returns the searchabilities as JSON string + * @throws IllegalArgumentException if request is null + * @throws ConstructorException if the request fails + */ + public String retrieveSearchabilitiesV2(SearchabilitiesV2GetRequest request) + throws ConstructorException { + if (request == null) { + throw new IllegalArgumentException("request is required"); + } + + try { + HttpUrl.Builder urlBuilder = + this.makeUrl(Arrays.asList("v2", "searchabilities")).newBuilder(); + + urlBuilder.addQueryParameter("section", request.getSection()); + + if (request.getPage() != null && request.getPage() > 0) { + urlBuilder.addQueryParameter("page", request.getPage().toString()); + } + if (request.getNumResultsPerPage() != null && request.getNumResultsPerPage() > 0) { + urlBuilder.addQueryParameter( + "num_results_per_page", request.getNumResultsPerPage().toString()); + } + if (request.getOffset() != null + && request.getOffset() > 0 + && request.getPage() == null) { + urlBuilder.addQueryParameter("offset", request.getOffset().toString()); + } + if (request.getName() != null && !request.getName().trim().isEmpty()) { + urlBuilder.addQueryParameter("name", request.getName()); + } + if (request.getFuzzySearchable() != null) { + urlBuilder.addQueryParameter( + "fuzzy_searchable", request.getFuzzySearchable().toString()); + } + if (request.getExactSearchable() != null) { + urlBuilder.addQueryParameter( + "exact_searchable", request.getExactSearchable().toString()); + } + if (request.getDisplayable() != null) { + urlBuilder.addQueryParameter("displayable", request.getDisplayable().toString()); + } + if (request.getMatchType() != null && !request.getMatchType().trim().isEmpty()) { + urlBuilder.addQueryParameter("match_type", request.getMatchType()); + } + if (request.getSortBy() != null && !request.getSortBy().trim().isEmpty()) { + urlBuilder.addQueryParameter("sort_by", request.getSortBy()); + } + if (request.getSortOrder() != null && !request.getSortOrder().trim().isEmpty()) { + urlBuilder.addQueryParameter("sort_order", request.getSortOrder()); + } + + HttpUrl url = urlBuilder.build(); + Request httpRequest = this.makeAuthorizedRequestBuilder().url(url).get().build(); + + Response response = clientWithRetry.newCall(httpRequest).execute(); + + return getResponseBody(response); + } catch (Exception exception) { + throw new ConstructorException(exception); + } + } + + /** + * Retrieves all searchabilities (v2) with default section "Products" + * + * @return returns the searchabilities as JSON string + * @throws ConstructorException if the request fails + */ + public String retrieveSearchabilitiesV2() throws ConstructorException { + return retrieveSearchabilitiesV2(new SearchabilitiesV2GetRequest()); + } + + /** + * Retrieves a single searchability by name (v2) + * + * @param searchabilityV2Request the searchability v2 request + * @return returns the searchability as JSON string + * @throws IllegalArgumentException if request is null + * @throws ConstructorException if the request fails + */ + public String retrieveSearchabilityV2(SearchabilityV2Request searchabilityV2Request) + throws ConstructorException { + if (searchabilityV2Request == null) { + throw new IllegalArgumentException("searchabilityV2Request is required"); + } + + try { + HttpUrl url = + this.makeUrl( + Arrays.asList( + "v2", + "searchabilities", + searchabilityV2Request.getName())) + .newBuilder() + .addQueryParameter("section", searchabilityV2Request.getSection()) + .build(); + + Request request = this.makeAuthorizedRequestBuilder().url(url).get().build(); + + Response response = clientWithRetry.newCall(request).execute(); + + return getResponseBody(response); + } catch (Exception exception) { + throw new ConstructorException(exception); + } + } + + /** + * Retrieves a single searchability by name (v2) + * + * @param name the name of the searchability field + * @param section the section to which the searchability belongs + * @return returns the searchability as JSON string + * @throws IllegalArgumentException if name is null or empty + * @throws ConstructorException if the request fails + */ + public String retrieveSearchabilityV2(String name, String section) throws ConstructorException { + return retrieveSearchabilityV2(new SearchabilityV2Request(name, section)); + } + + /** + * Retrieves a single searchability by name (v2) with default section "Products" + * + * @param name the name of the searchability field + * @return returns the searchability as JSON string + * @throws IllegalArgumentException if name is null or empty + * @throws ConstructorException if the request fails + */ + public String retrieveSearchabilityV2(String name) throws ConstructorException { + return retrieveSearchabilityV2(name, DEFAULT_SECTION); + } + + /** + * Creates or updates a single searchability (v2) + * + * @param searchabilityV2Request the searchability v2 request + * @return returns the created/updated searchability as JSON string + * @throws IllegalArgumentException if request is null + * @throws ConstructorException if the request fails + */ + public String createOrUpdateSearchabilityV2(SearchabilityV2Request searchabilityV2Request) + throws ConstructorException { + if (searchabilityV2Request == null) { + throw new IllegalArgumentException("searchabilityV2Request is required"); + } + + try { + HttpUrl.Builder urlBuilder = + this.makeUrl( + Arrays.asList( + "v2", + "searchabilities", + searchabilityV2Request.getName())) + .newBuilder() + .addQueryParameter("section", searchabilityV2Request.getSection()); + + if (searchabilityV2Request.getSkipRebuild() != null) { + urlBuilder.addQueryParameter( + "skip_rebuild", searchabilityV2Request.getSkipRebuild().toString()); + } + + HttpUrl url = urlBuilder.build(); + + String params = new Gson().toJson(searchabilityV2Request.getSearchability()); + RequestBody body = + RequestBody.create(params, MediaType.parse("application/json; charset=utf-8")); + Request request = this.makeAuthorizedRequestBuilder().url(url).patch(body).build(); + + Response response = client.newCall(request).execute(); + + return getResponseBody(response); + } catch (Exception exception) { + throw new ConstructorException(exception); + } + } + + /** + * Creates or updates multiple searchabilities (v2) - bulk operation + * + * @param searchabilitiesV2Request the searchabilities v2 request + * @return returns the created/updated searchabilities as JSON string + * @throws IllegalArgumentException if request is null + * @throws ConstructorException if the request fails + */ + public String createOrUpdateSearchabilitiesV2(SearchabilitiesV2Request searchabilitiesV2Request) + throws ConstructorException { + if (searchabilitiesV2Request == null) { + throw new IllegalArgumentException("searchabilitiesV2Request is required"); + } + + try { + HttpUrl.Builder urlBuilder = + this.makeUrl(Arrays.asList("v2", "searchabilities")) + .newBuilder() + .addQueryParameter("section", searchabilitiesV2Request.getSection()); + + if (searchabilitiesV2Request.getSkipRebuild() != null) { + urlBuilder.addQueryParameter( + "skip_rebuild", searchabilitiesV2Request.getSkipRebuild().toString()); + } + + HttpUrl url = urlBuilder.build(); + + Map bodyMap = new HashMap<>(); + bodyMap.put("searchabilities", searchabilitiesV2Request.getSearchabilities()); + String params = new Gson().toJson(bodyMap); + RequestBody body = + RequestBody.create(params, MediaType.parse("application/json; charset=utf-8")); + Request request = this.makeAuthorizedRequestBuilder().url(url).patch(body).build(); + + Response response = client.newCall(request).execute(); + + return getResponseBody(response); + } catch (Exception exception) { + throw new ConstructorException(exception); + } + } + + /** + * Deletes a single searchability (v2) + * + * @param searchabilityV2Request the searchability v2 request + * @return returns the deleted searchability as JSON string + * @throws IllegalArgumentException if request is null + * @throws ConstructorException if the request fails + */ + public String deleteSearchabilityV2(SearchabilityV2Request searchabilityV2Request) + throws ConstructorException { + if (searchabilityV2Request == null) { + throw new IllegalArgumentException("searchabilityV2Request is required"); + } + + try { + HttpUrl.Builder urlBuilder = + this.makeUrl( + Arrays.asList( + "v2", + "searchabilities", + searchabilityV2Request.getName())) + .newBuilder() + .addQueryParameter("section", searchabilityV2Request.getSection()); + + if (searchabilityV2Request.getSkipRebuild() != null) { + urlBuilder.addQueryParameter( + "skip_rebuild", searchabilityV2Request.getSkipRebuild().toString()); + } + + HttpUrl url = urlBuilder.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 single searchability (v2) + * + * @param name the name of the searchability field + * @param section the section to which the searchability belongs + * @return returns the deleted searchability as JSON string + * @throws IllegalArgumentException if name is null or empty + * @throws ConstructorException if the request fails + */ + public String deleteSearchabilityV2(String name, String section) throws ConstructorException { + return deleteSearchabilityV2(new SearchabilityV2Request(name, section)); + } + + /** + * Deletes a single searchability (v2) with default section "Products" + * + * @param name the name of the searchability field + * @return returns the deleted searchability as JSON string + * @throws IllegalArgumentException if name is null or empty + * @throws ConstructorException if the request fails + */ + public String deleteSearchabilityV2(String name) throws ConstructorException { + return deleteSearchabilityV2(name, DEFAULT_SECTION); + } + + /** + * Deletes multiple searchabilities (v2) - bulk operation + * + * @param request the searchabilities v2 delete request with names to delete + * @return returns the deleted searchabilities as JSON string + * @throws IllegalArgumentException if request is null + * @throws ConstructorException if the request fails + */ + public String deleteSearchabilitiesV2(SearchabilitiesV2DeleteRequest request) + throws ConstructorException { + if (request == null) { + throw new IllegalArgumentException("request is required"); + } + + try { + HttpUrl.Builder urlBuilder = + this.makeUrl(Arrays.asList("v2", "searchabilities")) + .newBuilder() + .addQueryParameter("section", request.getSection()); + + if (request.getSkipRebuild() != null) { + urlBuilder.addQueryParameter("skip_rebuild", request.getSkipRebuild().toString()); + } + + HttpUrl url = urlBuilder.build(); + + List> searchabilitiesBody = new ArrayList<>(); + for (String name : request.getSearchabilityNames()) { + Map item = new HashMap<>(); + item.put("name", name); + searchabilitiesBody.add(item); + } + + Map bodyMap = new HashMap<>(); + bodyMap.put("searchabilities", searchabilitiesBody); + String params = new Gson().toJson(bodyMap); + RequestBody httpBody = + RequestBody.create(params, MediaType.parse("application/json; charset=utf-8")); + Request httpRequest = + this.makeAuthorizedRequestBuilder().url(url).delete(httpBody).build(); + + Response response = client.newCall(httpRequest).execute(); + + return getResponseBody(response); + } catch (Exception exception) { + throw new ConstructorException(exception); + } + } } diff --git a/constructorio-client/src/main/java/io/constructor/client/FacetConfigurationV2Request.java b/constructorio-client/src/main/java/io/constructor/client/FacetConfigurationV2Request.java new file mode 100644 index 00000000..dcbced01 --- /dev/null +++ b/constructorio-client/src/main/java/io/constructor/client/FacetConfigurationV2Request.java @@ -0,0 +1,69 @@ +package io.constructor.client; + +import io.constructor.client.models.FacetConfigurationV2; + +/** + * Constructor.io FacetConfiguration V2 Request. + * + *

This request class is used for v2 facet configuration operations which require + * path_in_metadata to be specified. + */ +public class FacetConfigurationV2Request { + private FacetConfigurationV2 facetConfiguration; + private String section; + + /** + * Creates a facet configuration v2 request + * + * @param facetConfiguration the facet configuration to be created/updated + * @param section the section to which the facet belongs + */ + public FacetConfigurationV2Request(FacetConfigurationV2 facetConfiguration, String section) { + if (facetConfiguration == null) { + throw new IllegalArgumentException("facetConfiguration is required"); + } + if (section == null) { + throw new IllegalArgumentException("section is required"); + } + + this.facetConfiguration = facetConfiguration; + this.section = section; + } + + /** + * Creates a facet configuration v2 request with default section "Products" + * + * @param facetConfiguration the facet configuration to be created/updated + */ + public FacetConfigurationV2Request(FacetConfigurationV2 facetConfiguration) { + this(facetConfiguration, ConstructorIO.DEFAULT_SECTION); + } + + /** + * @param facetConfiguration the facet configuration to be created/updated + */ + public void setFacetConfiguration(FacetConfigurationV2 facetConfiguration) { + this.facetConfiguration = facetConfiguration; + } + + /** + * @return the facet configuration to be created/updated + */ + public FacetConfigurationV2 getFacetConfiguration() { + return facetConfiguration; + } + + /** + * @param section the section to set + */ + public void setSection(String section) { + this.section = section; + } + + /** + * @return the section + */ + public String getSection() { + return section; + } +} diff --git a/constructorio-client/src/main/java/io/constructor/client/FacetConfigurationsV2Request.java b/constructorio-client/src/main/java/io/constructor/client/FacetConfigurationsV2Request.java new file mode 100644 index 00000000..75da66c2 --- /dev/null +++ b/constructorio-client/src/main/java/io/constructor/client/FacetConfigurationsV2Request.java @@ -0,0 +1,70 @@ +package io.constructor.client; + +import io.constructor.client.models.FacetConfigurationV2; +import java.util.List; + +/** + * Constructor.io FacetConfigurations V2 Request. + * + *

This request class is used for bulk v2 facet configuration operations (PATCH /v2/facets). + */ +public class FacetConfigurationsV2Request { + private List facetConfigurations; + private String section; + + /** + * Creates a facet configurations v2 request for bulk update + * + * @param facetConfigurations the list of facet configurations to be updated + * @param section the section to which the facets belong + */ + public FacetConfigurationsV2Request( + List facetConfigurations, String section) { + if (facetConfigurations == null || facetConfigurations.isEmpty()) { + throw new IllegalArgumentException("facetConfigurations is required"); + } + if (section == null) { + throw new IllegalArgumentException("section is required"); + } + + this.facetConfigurations = facetConfigurations; + this.section = section; + } + + /** + * Creates a facet configurations v2 request with default section "Products" + * + * @param facetConfigurations the list of facet configurations to be updated + */ + public FacetConfigurationsV2Request(List facetConfigurations) { + this(facetConfigurations, ConstructorIO.DEFAULT_SECTION); + } + + /** + * @return the list of facet configurations + */ + public List getFacetConfigurations() { + return facetConfigurations; + } + + /** + * @param facetConfigurations the facet configurations to set + */ + public void setFacetConfigurations(List facetConfigurations) { + this.facetConfigurations = facetConfigurations; + } + + /** + * @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/SearchabilitiesV2DeleteRequest.java b/constructorio-client/src/main/java/io/constructor/client/SearchabilitiesV2DeleteRequest.java new file mode 100644 index 00000000..942f52dd --- /dev/null +++ b/constructorio-client/src/main/java/io/constructor/client/SearchabilitiesV2DeleteRequest.java @@ -0,0 +1,84 @@ +package io.constructor.client; + +import java.util.List; + +/** + * Constructor.io Searchabilities V2 DELETE Request. + * + *

This request class is used for bulk deletion of v2 searchability configurations (DELETE + * /v2/searchabilities). + */ +public class SearchabilitiesV2DeleteRequest { + private List searchabilityNames; + private String section; + private Boolean skipRebuild; + + /** + * Creates a searchabilities v2 DELETE request + * + * @param searchabilityNames the list of searchability names to delete + * @param section the section to which the searchabilities belong + */ + public SearchabilitiesV2DeleteRequest(List searchabilityNames, String section) { + if (searchabilityNames == null || searchabilityNames.isEmpty()) { + throw new IllegalArgumentException("searchabilityNames is required"); + } + if (section == null) { + throw new IllegalArgumentException("section is required"); + } + + this.searchabilityNames = searchabilityNames; + this.section = section; + } + + /** + * Creates a searchabilities v2 DELETE request with default section "Products" + * + * @param searchabilityNames the list of searchability names to delete + */ + public SearchabilitiesV2DeleteRequest(List searchabilityNames) { + this(searchabilityNames, ConstructorIO.DEFAULT_SECTION); + } + + /** + * @return the list of searchability names to delete + */ + public List getSearchabilityNames() { + return searchabilityNames; + } + + /** + * @param searchabilityNames the searchability names to set + */ + public void setSearchabilityNames(List searchabilityNames) { + this.searchabilityNames = searchabilityNames; + } + + /** + * @return the section + */ + public String getSection() { + return section; + } + + /** + * @param section the section to set + */ + public void setSection(String section) { + this.section = section; + } + + /** + * @return whether to skip index rebuild + */ + public Boolean getSkipRebuild() { + return skipRebuild; + } + + /** + * @param skipRebuild whether to skip index rebuild + */ + public void setSkipRebuild(Boolean skipRebuild) { + this.skipRebuild = skipRebuild; + } +} diff --git a/constructorio-client/src/main/java/io/constructor/client/SearchabilitiesV2GetRequest.java b/constructorio-client/src/main/java/io/constructor/client/SearchabilitiesV2GetRequest.java new file mode 100644 index 00000000..3c9c3725 --- /dev/null +++ b/constructorio-client/src/main/java/io/constructor/client/SearchabilitiesV2GetRequest.java @@ -0,0 +1,196 @@ +package io.constructor.client; + +/** + * Constructor.io Searchabilities V2 GET Request. + * + *

This request class is used for listing v2 searchability configurations (GET + * /v2/searchabilities). + */ +public class SearchabilitiesV2GetRequest { + private String section; + + // Pagination parameters + private Integer page; + private Integer numResultsPerPage; + private Integer offset; + + // Filter parameters + private String name; + private Boolean fuzzySearchable; + private Boolean exactSearchable; + private Boolean displayable; + private String matchType; + private String sortBy; + private String sortOrder; + + /** + * Creates a searchabilities v2 GET request + * + * @param section the section to which the searchabilities belong + */ + public SearchabilitiesV2GetRequest(String section) { + if (section == null) { + throw new IllegalArgumentException("section is required"); + } + this.section = section; + } + + /** Creates a searchabilities v2 GET request with default section "Products" */ + public SearchabilitiesV2GetRequest() { + this.section = ConstructorIO.DEFAULT_SECTION; + } + + /** + * @return the section + */ + public String getSection() { + return section; + } + + /** + * @param section the section to set + */ + public void setSection(String section) { + this.section = section; + } + + /** + * @return the page number + */ + public Integer getPage() { + return page; + } + + /** + * @param page the page number to set + */ + public void setPage(Integer page) { + this.page = page; + } + + /** + * @return the number of results per page + */ + public Integer getNumResultsPerPage() { + return numResultsPerPage; + } + + /** + * @param numResultsPerPage the number of results per page to set + */ + public void setNumResultsPerPage(Integer numResultsPerPage) { + this.numResultsPerPage = numResultsPerPage; + } + + /** + * @return the offset + */ + public Integer getOffset() { + return offset; + } + + /** + * @param offset the offset to set + */ + public void setOffset(Integer offset) { + this.offset = offset; + } + + /** + * @return the name filter + */ + public String getName() { + return name; + } + + /** + * @param name the name filter to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the fuzzy searchable filter + */ + public Boolean getFuzzySearchable() { + return fuzzySearchable; + } + + /** + * @param fuzzySearchable the fuzzy searchable filter to set + */ + public void setFuzzySearchable(Boolean fuzzySearchable) { + this.fuzzySearchable = fuzzySearchable; + } + + /** + * @return the exact searchable filter + */ + public Boolean getExactSearchable() { + return exactSearchable; + } + + /** + * @param exactSearchable the exact searchable filter to set + */ + public void setExactSearchable(Boolean exactSearchable) { + this.exactSearchable = exactSearchable; + } + + /** + * @return the displayable filter + */ + public Boolean getDisplayable() { + return displayable; + } + + /** + * @param displayable the displayable filter to set + */ + public void setDisplayable(Boolean displayable) { + this.displayable = displayable; + } + + /** + * @return the match type filter + */ + public String getMatchType() { + return matchType; + } + + /** + * @param matchType the match type filter to set + */ + public void setMatchType(String matchType) { + this.matchType = matchType; + } + + /** + * @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 + */ + public String getSortOrder() { + return sortOrder; + } + + /** + * @param sortOrder the sort order to set + */ + public void setSortOrder(String sortOrder) { + this.sortOrder = sortOrder; + } +} diff --git a/constructorio-client/src/main/java/io/constructor/client/SearchabilitiesV2Request.java b/constructorio-client/src/main/java/io/constructor/client/SearchabilitiesV2Request.java new file mode 100644 index 00000000..f601783b --- /dev/null +++ b/constructorio-client/src/main/java/io/constructor/client/SearchabilitiesV2Request.java @@ -0,0 +1,88 @@ +package io.constructor.client; + +import io.constructor.client.models.SearchabilityV2; +import java.util.List; + +/** + * Constructor.io Searchabilities V2 Request. + * + *

This request class is used for bulk v2 searchability configuration operations (PATCH + * /v2/searchabilities). + * + *

For GET operations, use {@link SearchabilitiesV2GetRequest}. For DELETE operations, use {@link + * SearchabilitiesV2DeleteRequest}. + */ +public class SearchabilitiesV2Request { + private List searchabilities; + private String section; + private Boolean skipRebuild; + + /** + * Creates a searchabilities v2 request for bulk update (PATCH) + * + * @param searchabilities the list of searchability configurations + * @param section the section to which the searchabilities belong + */ + public SearchabilitiesV2Request(List searchabilities, String section) { + if (searchabilities == null || searchabilities.isEmpty()) { + throw new IllegalArgumentException("searchabilities is required"); + } + if (section == null) { + throw new IllegalArgumentException("section is required"); + } + + this.searchabilities = searchabilities; + this.section = section; + } + + /** + * Creates a searchabilities v2 request with default section "Products" + * + * @param searchabilities the list of searchability configurations + */ + public SearchabilitiesV2Request(List searchabilities) { + this(searchabilities, ConstructorIO.DEFAULT_SECTION); + } + + /** + * @return the list of searchability configurations + */ + public List getSearchabilities() { + return searchabilities; + } + + /** + * @param searchabilities the searchabilities to set + */ + public void setSearchabilities(List searchabilities) { + this.searchabilities = searchabilities; + } + + /** + * @return the section + */ + public String getSection() { + return section; + } + + /** + * @param section the section to set + */ + public void setSection(String section) { + this.section = section; + } + + /** + * @return whether to skip index rebuild + */ + public Boolean getSkipRebuild() { + return skipRebuild; + } + + /** + * @param skipRebuild whether to skip index rebuild + */ + public void setSkipRebuild(Boolean skipRebuild) { + this.skipRebuild = skipRebuild; + } +} diff --git a/constructorio-client/src/main/java/io/constructor/client/SearchabilityV2Request.java b/constructorio-client/src/main/java/io/constructor/client/SearchabilityV2Request.java new file mode 100644 index 00000000..771d9def --- /dev/null +++ b/constructorio-client/src/main/java/io/constructor/client/SearchabilityV2Request.java @@ -0,0 +1,121 @@ +package io.constructor.client; + +import io.constructor.client.models.SearchabilityV2; + +/** + * Constructor.io Searchability V2 Request. + * + *

This request class is used for single v2 searchability configuration operations + * (GET/PATCH/DELETE /v2/searchabilities/{name}). + */ +public class SearchabilityV2Request { + private SearchabilityV2 searchability; + private String name; + private String section; + private Boolean skipRebuild; + + /** + * Creates a searchability v2 request + * + * @param searchability the searchability configuration to be created/updated + * @param name the name of the searchability field + * @param section the section to which the searchability belongs + */ + public SearchabilityV2Request(SearchabilityV2 searchability, String name, String section) { + if (name == null || name.trim().isEmpty()) { + throw new IllegalArgumentException("name is required"); + } + if (section == null) { + throw new IllegalArgumentException("section is required"); + } + + this.searchability = searchability; + this.name = name; + this.section = section; + } + + /** + * Creates a searchability v2 request with default section "Products" + * + * @param searchability the searchability configuration to be created/updated + * @param name the name of the searchability field + */ + public SearchabilityV2Request(SearchabilityV2 searchability, String name) { + this(searchability, name, ConstructorIO.DEFAULT_SECTION); + } + + /** + * Creates a searchability v2 request for GET/DELETE operations (no body needed) + * + * @param name the name of the searchability field + * @param section the section to which the searchability belongs + */ + public SearchabilityV2Request(String name, String section) { + this(null, name, section); + } + + /** + * Creates a searchability v2 request for GET/DELETE operations with default section + * + * @param name the name of the searchability field + */ + public SearchabilityV2Request(String name) { + this(null, name, ConstructorIO.DEFAULT_SECTION); + } + + /** + * @return the searchability configuration + */ + public SearchabilityV2 getSearchability() { + return searchability; + } + + /** + * @param searchability the searchability configuration to set + */ + public void setSearchability(SearchabilityV2 searchability) { + this.searchability = searchability; + } + + /** + * @return the name of the searchability field + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the section + */ + public String getSection() { + return section; + } + + /** + * @param section the section to set + */ + public void setSection(String section) { + this.section = section; + } + + /** + * @return whether to skip index rebuild + */ + public Boolean getSkipRebuild() { + return skipRebuild; + } + + /** + * @param skipRebuild whether to skip index rebuild + */ + public void setSkipRebuild(Boolean skipRebuild) { + this.skipRebuild = skipRebuild; + } +} diff --git a/constructorio-client/src/main/java/io/constructor/client/models/FacetConfigurationV2.java b/constructorio-client/src/main/java/io/constructor/client/models/FacetConfigurationV2.java new file mode 100644 index 00000000..31d0e527 --- /dev/null +++ b/constructorio-client/src/main/java/io/constructor/client/models/FacetConfigurationV2.java @@ -0,0 +1,280 @@ +package io.constructor.client.models; + +import com.google.gson.annotations.SerializedName; +import java.util.List; +import java.util.Map; + +/** + * Constructor.io Facet Configuration V2 model. + * + *

This class represents the v2 facet configuration with path_in_metadata support. Uses + * Gson/Reflection to load data in. + */ +public class FacetConfigurationV2 { + + @SerializedName("name") + private String name; + + @SerializedName("path_in_metadata") + private String pathInMetadata; + + @SerializedName("type") + private String type; + + @SerializedName("display_name") + private String displayName; + + @SerializedName("sort_order") + private String sortOrder; + + @SerializedName("sort_descending") + private Boolean sortDescending; + + @SerializedName("range_type") + private String rangeType; + + @SerializedName("range_format") + private String rangeFormat; + + @SerializedName("range_inclusive") + private String rangeInclusive; + + @SerializedName("range_limits") + private List rangeLimits; + + @SerializedName("match_type") + private String matchType; + + @SerializedName("position") + private Integer position; + + @SerializedName("hidden") + private Boolean hidden; + + @SerializedName("protected") + private Boolean isProtected; + + @SerializedName("countable") + private Boolean countable; + + @SerializedName("options_limit") + private Integer optionsLimit; + + @SerializedName("data") + private Map data; + + @SerializedName("created_at") + private String createdAt; + + @SerializedName("updated_at") + private String updatedAt; + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @return the path in metadata + */ + public String getPathInMetadata() { + return pathInMetadata; + } + + /** + * @return the type + */ + public String getType() { + return type; + } + + /** + * @return the displayName + */ + public String getDisplayName() { + return displayName; + } + + /** + * @return the sortOrder + */ + public String getSortOrder() { + return sortOrder; + } + + /** + * @return the sortDescending + */ + public Boolean getSortDescending() { + return sortDescending; + } + + /** + * @return the rangeType + */ + public String getRangeType() { + return rangeType; + } + + /** + * @return the rangeFormat + */ + public String getRangeFormat() { + return rangeFormat; + } + + /** + * @return the rangeInclusive + */ + public String getRangeInclusive() { + return rangeInclusive; + } + + /** + * @return the rangeLimits + */ + public List getRangeLimits() { + return rangeLimits; + } + + /** + * @return the matchType + */ + public String getMatchType() { + return matchType; + } + + /** + * @return the position + */ + public Integer getPosition() { + return position; + } + + /** + * @return the hidden + */ + public Boolean getHidden() { + return hidden; + } + + /** + * @return the isProtected + */ + public Boolean getIsProtected() { + return isProtected; + } + + /** + * @return the countable + */ + public Boolean getCountable() { + return countable; + } + + /** + * @return the optionsLimit + */ + public Integer getOptionsLimit() { + return optionsLimit; + } + + /** + * @return the data + */ + public Map getData() { + return data; + } + + /** + * @return the creation timestamp + */ + public String getCreatedAt() { + return createdAt; + } + + /** + * @return the last update timestamp + */ + public String getUpdatedAt() { + return updatedAt; + } + + public void setName(String name) { + this.name = name; + } + + public void setPathInMetadata(String pathInMetadata) { + this.pathInMetadata = pathInMetadata; + } + + public void setType(String type) { + this.type = type; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public void setSortOrder(String sortOrder) { + this.sortOrder = sortOrder; + } + + public void setSortDescending(Boolean sortDescending) { + this.sortDescending = sortDescending; + } + + public void setRangeType(String rangeType) { + this.rangeType = rangeType; + } + + public void setRangeFormat(String rangeFormat) { + this.rangeFormat = rangeFormat; + } + + public void setRangeInclusive(String rangeInclusive) { + this.rangeInclusive = rangeInclusive; + } + + public void setRangeLimits(List rangeLimits) { + this.rangeLimits = rangeLimits; + } + + public void setMatchType(String matchType) { + this.matchType = matchType; + } + + public void setPosition(Integer position) { + this.position = position; + } + + public void setHidden(Boolean hidden) { + this.hidden = hidden; + } + + public void setIsProtected(Boolean isProtected) { + this.isProtected = isProtected; + } + + public void setCountable(Boolean countable) { + this.countable = countable; + } + + public void setOptionsLimit(Integer optionsLimit) { + this.optionsLimit = optionsLimit; + } + + public void setData(Map data) { + this.data = data; + } + + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + + public void setUpdatedAt(String updatedAt) { + this.updatedAt = updatedAt; + } +} diff --git a/constructorio-client/src/main/java/io/constructor/client/models/SearchabilityV2.java b/constructorio-client/src/main/java/io/constructor/client/models/SearchabilityV2.java new file mode 100644 index 00000000..66c986dc --- /dev/null +++ b/constructorio-client/src/main/java/io/constructor/client/models/SearchabilityV2.java @@ -0,0 +1,110 @@ +package io.constructor.client.models; + +import com.google.gson.annotations.SerializedName; + +/** + * Constructor.io Searchability V2 model. + * + *

This class represents the v2 searchability configuration. Uses Gson/Reflection to load data + * in. + */ +public class SearchabilityV2 { + + @SerializedName("name") + private String name; + + @SerializedName("fuzzy_searchable") + private Boolean fuzzySearchable; + + @SerializedName("exact_searchable") + private Boolean exactSearchable; + + @SerializedName("displayable") + private Boolean displayable; + + @SerializedName("hidden") + private Boolean hidden; + + @SerializedName("created_at") + private String createdAt; + + @SerializedName("updated_at") + private String updatedAt; + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @return whether terms can be fuzzy searchable + */ + public Boolean getFuzzySearchable() { + return fuzzySearchable; + } + + /** + * @return whether terms can be exact searchable + */ + public Boolean getExactSearchable() { + return exactSearchable; + } + + /** + * @return whether the field is displayable in the response + */ + public Boolean getDisplayable() { + return displayable; + } + + /** + * @return whether the field is hidden by default + */ + public Boolean getHidden() { + return hidden; + } + + /** + * @return the creation timestamp + */ + public String getCreatedAt() { + return createdAt; + } + + /** + * @return the last update timestamp + */ + public String getUpdatedAt() { + return updatedAt; + } + + public void setName(String name) { + this.name = name; + } + + public void setFuzzySearchable(Boolean fuzzySearchable) { + this.fuzzySearchable = fuzzySearchable; + } + + public void setExactSearchable(Boolean exactSearchable) { + this.exactSearchable = exactSearchable; + } + + public void setDisplayable(Boolean displayable) { + this.displayable = displayable; + } + + public void setHidden(Boolean hidden) { + this.hidden = hidden; + } + + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + + public void setUpdatedAt(String updatedAt) { + this.updatedAt = updatedAt; + } +} diff --git a/constructorio-client/src/test/java/io/constructor/client/ConstructorIOFacetConfigurationV2Test.java b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOFacetConfigurationV2Test.java new file mode 100644 index 00000000..93db0898 --- /dev/null +++ b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOFacetConfigurationV2Test.java @@ -0,0 +1,282 @@ +package io.constructor.client; + +import static org.junit.Assert.*; + +import com.google.gson.Gson; +import io.constructor.client.models.FacetConfigurationV2; +import java.util.ArrayList; +import java.util.Arrays; +import org.json.JSONObject; +import org.junit.AfterClass; +import org.junit.Test; + +public class ConstructorIOFacetConfigurationV2Test { + + private static String token = System.getenv("TEST_API_TOKEN"); + private static String apiKey = System.getenv("TEST_CATALOG_API_KEY"); + private static ArrayList facetsToCleanup = new ArrayList<>(); + + private void addFacetToCleanupArray(String facetName, String section) { + if (section == null) { + section = ConstructorIO.DEFAULT_SECTION; + } + facetsToCleanup.add(facetName + "|" + section); + } + + private void addFacetToCleanupArray(String facetName) { + addFacetToCleanupArray(facetName, ConstructorIO.DEFAULT_SECTION); + } + + @AfterClass + public static void cleanupFacets() throws ConstructorException { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + for (String facet : facetsToCleanup) { + String[] parts = facet.split("\\|"); + String facetName = parts[0]; + String section = parts[1]; + + try { + constructor.deleteFacetConfigurationV2(facetName, section); + } catch (ConstructorException e) { + System.err.println("Warning: Failed to clean up facet: " + facetName); + } + } + } + + @Test + public void testCreateFacetConfigurationV2() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + String string = Utils.getTestResource("facet.configuration.v2.json"); + FacetConfigurationV2 facetConfiguration = + new Gson().fromJson(string, FacetConfigurationV2.class); + + facetConfiguration.setName("testFacetV2"); + facetConfiguration.setPathInMetadata("testFacetV2"); + FacetConfigurationV2Request request = + new FacetConfigurationV2Request(facetConfiguration, ConstructorIO.DEFAULT_SECTION); + + String response = constructor.createFacetConfigurationV2(request); + JSONObject jsonObj = new JSONObject(response); + JSONObject loadedJsonObj = new JSONObject(string); + + assertEquals("testFacetV2", jsonObj.get("name")); + assertEquals("testFacetV2", jsonObj.get("path_in_metadata")); + assertEquals(loadedJsonObj.get("type"), jsonObj.get("type")); + assertEquals(loadedJsonObj.get("display_name"), jsonObj.get("display_name")); + assertEquals(loadedJsonObj.get("sort_order"), jsonObj.get("sort_order")); + assertEquals(loadedJsonObj.get("sort_descending"), jsonObj.get("sort_descending")); + assertEquals(loadedJsonObj.get("match_type"), jsonObj.get("match_type")); + assertEquals(loadedJsonObj.get("position"), jsonObj.get("position")); + assertEquals(loadedJsonObj.get("hidden"), jsonObj.get("hidden")); + assertEquals(loadedJsonObj.get("protected"), jsonObj.get("protected")); + assertEquals(loadedJsonObj.get("countable"), jsonObj.get("countable")); + assertEquals(loadedJsonObj.get("options_limit"), jsonObj.get("options_limit")); + addFacetToCleanupArray("testFacetV2"); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateFacetConfigurationV2WithNullRequestThrowsException() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + constructor.createFacetConfigurationV2(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateFacetConfigurationV2WithNullConfigurationThrowsException() { + new FacetConfigurationV2Request(null, ConstructorIO.DEFAULT_SECTION); + } + + @Test + public void testCreateFacetConfigurationV2WithDifferentSection() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + String string = Utils.getTestResource("facet.configuration.v2.json"); + FacetConfigurationV2 config = new Gson().fromJson(string, FacetConfigurationV2.class); + config.setName("testFacetV2Section"); + config.setPathInMetadata("testFacetV2Section"); + + FacetConfigurationV2Request request = + new FacetConfigurationV2Request(config, "Search Suggestions"); + + String response = constructor.createFacetConfigurationV2(request); + JSONObject jsonObj = new JSONObject(response); + + assertEquals("testFacetV2Section", jsonObj.getString("name")); + assertEquals("testFacetV2Section", jsonObj.getString("path_in_metadata")); + addFacetToCleanupArray("testFacetV2Section", "Search Suggestions"); + } + + @Test + public void testRetrieveFacetConfigurationV2() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create a facet first + String string = Utils.getTestResource("facet.configuration.v2.json"); + FacetConfigurationV2 facetConfig = new Gson().fromJson(string, FacetConfigurationV2.class); + facetConfig.setName("testRetrieveFacetV2"); + facetConfig.setPathInMetadata("testRetrieveFacetV2"); + + constructor.createFacetConfigurationV2( + new FacetConfigurationV2Request(facetConfig, ConstructorIO.DEFAULT_SECTION)); + + // Retrieve the facet + String retrieveResponse = + constructor.retrieveFacetConfigurationV2( + "testRetrieveFacetV2", ConstructorIO.DEFAULT_SECTION); + JSONObject jsonObj = new JSONObject(retrieveResponse); + + assertEquals("testRetrieveFacetV2", jsonObj.get("name")); + assertEquals("testRetrieveFacetV2", jsonObj.get("path_in_metadata")); + addFacetToCleanupArray("testRetrieveFacetV2"); + } + + @Test + public void testRetrieveFacetConfigurationsV2() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Retrieve all facets + String retrieveResponse = + constructor.retrieveFacetConfigurationsV2( + ConstructorIO.DEFAULT_SECTION, null, null); + JSONObject jsonObj = new JSONObject(retrieveResponse); + + assertTrue("Response should have facets array", jsonObj.has("facets")); + } + + @Test + public void testUpdateFacetConfigurationV2() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create a facet first + String string = Utils.getTestResource("facet.configuration.v2.json"); + FacetConfigurationV2 facetConfig = new Gson().fromJson(string, FacetConfigurationV2.class); + facetConfig.setName("testUpdateFacetV2"); + facetConfig.setPathInMetadata("testUpdateFacetV2"); + + constructor.createFacetConfigurationV2( + new FacetConfigurationV2Request(facetConfig, ConstructorIO.DEFAULT_SECTION)); + + // Update the facet + facetConfig.setDisplayName("Updated Brand Name"); + String updateResponse = + constructor.updateFacetConfigurationV2( + new FacetConfigurationV2Request( + facetConfig, ConstructorIO.DEFAULT_SECTION)); + JSONObject jsonObj = new JSONObject(updateResponse); + + assertEquals("Updated Brand Name", jsonObj.get("display_name")); + addFacetToCleanupArray("testUpdateFacetV2"); + } + + @Test + public void testDeleteFacetConfigurationV2WithFacetNameAndSection() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create a facet first + String string = Utils.getTestResource("facet.configuration.v2.json"); + FacetConfigurationV2 facetConfig = new Gson().fromJson(string, FacetConfigurationV2.class); + facetConfig.setName("testDeleteFacetV2"); + facetConfig.setPathInMetadata("testDeleteFacetV2"); + + constructor.createFacetConfigurationV2( + new FacetConfigurationV2Request(facetConfig, ConstructorIO.DEFAULT_SECTION)); + + // Delete the facet + String deleteResponse = + constructor.deleteFacetConfigurationV2( + "testDeleteFacetV2", ConstructorIO.DEFAULT_SECTION); + JSONObject jsonObj = new JSONObject(deleteResponse); + + assertEquals("testDeleteFacetV2", jsonObj.get("name")); + } + + @Test + public void testDeleteFacetConfigurationV2WithDefaultSection() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create a facet first + String string = Utils.getTestResource("facet.configuration.v2.json"); + FacetConfigurationV2 facetConfig = new Gson().fromJson(string, FacetConfigurationV2.class); + facetConfig.setName("testDefaultSectionFacetV2"); + facetConfig.setPathInMetadata("testDefaultSectionFacetV2"); + + constructor.createFacetConfigurationV2( + new FacetConfigurationV2Request(facetConfig, ConstructorIO.DEFAULT_SECTION)); + + // Delete the facet + String deleteResponse = constructor.deleteFacetConfigurationV2("testDefaultSectionFacetV2"); + JSONObject jsonObj = new JSONObject(deleteResponse); + + assertEquals("testDefaultSectionFacetV2", jsonObj.get("name")); + } + + @Test + public void testDeleteFacetConfigurationV2WithFacetConfiguration() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create a facet first + String string = Utils.getTestResource("facet.configuration.v2.json"); + FacetConfigurationV2 facetConfig = new Gson().fromJson(string, FacetConfigurationV2.class); + facetConfig.setName("testDeleteWithFacetConfigurationV2"); + facetConfig.setPathInMetadata("testDeleteWithFacetConfigurationV2"); + + FacetConfigurationV2Request request = + new FacetConfigurationV2Request(facetConfig, ConstructorIO.DEFAULT_SECTION); + constructor.createFacetConfigurationV2(request); + + // Delete the facet + String deleteResponse = constructor.deleteFacetConfigurationV2(request); + JSONObject jsonObj = new JSONObject(deleteResponse); + + assertEquals("testDeleteWithFacetConfigurationV2", jsonObj.get("name")); + } + + @Test(expected = ConstructorException.class) + public void testDeleteNonExistentFacetV2ThrowsException() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + constructor.deleteFacetConfigurationV2("nonExistentFacetV2", ConstructorIO.DEFAULT_SECTION); + } + + @Test + public void testUpdateFacetConfigurationsV2Bulk() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create two facets first + String string = Utils.getTestResource("facet.configuration.v2.json"); + FacetConfigurationV2 facetConfig1 = new Gson().fromJson(string, FacetConfigurationV2.class); + facetConfig1.setName("testBulkFacetV2_1"); + facetConfig1.setPathInMetadata("testBulkFacetV2_1"); + + FacetConfigurationV2 facetConfig2 = new Gson().fromJson(string, FacetConfigurationV2.class); + facetConfig2.setName("testBulkFacetV2_2"); + facetConfig2.setPathInMetadata("testBulkFacetV2_2"); + + constructor.createFacetConfigurationV2( + new FacetConfigurationV2Request(facetConfig1, ConstructorIO.DEFAULT_SECTION)); + constructor.createFacetConfigurationV2( + new FacetConfigurationV2Request(facetConfig2, ConstructorIO.DEFAULT_SECTION)); + + // Update both facets + facetConfig1.setDisplayName("Bulk Updated 1"); + facetConfig2.setDisplayName("Bulk Updated 2"); + + FacetConfigurationsV2Request bulkRequest = + new FacetConfigurationsV2Request( + Arrays.asList(facetConfig1, facetConfig2), ConstructorIO.DEFAULT_SECTION); + + String updateResponse = constructor.updateFacetConfigurationsV2(bulkRequest); + JSONObject jsonObj = new JSONObject(updateResponse); + + assertTrue("Response should have facets array", jsonObj.has("facets")); + addFacetToCleanupArray("testBulkFacetV2_1"); + addFacetToCleanupArray("testBulkFacetV2_2"); + } + + @Test + public void testFacetConfigurationV2DefaultValues() { + FacetConfigurationV2 config = new FacetConfigurationV2(); + assertNull("Position should default to null", config.getPosition()); + assertNull("Options limit should default to null", config.getOptionsLimit()); + assertNull("Path in metadata should default to null", config.getPathInMetadata()); + } +} diff --git a/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSearchabilityV2Test.java b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSearchabilityV2Test.java new file mode 100644 index 00000000..36b54a85 --- /dev/null +++ b/constructorio-client/src/test/java/io/constructor/client/ConstructorIOSearchabilityV2Test.java @@ -0,0 +1,326 @@ +package io.constructor.client; + +import static org.junit.Assert.*; + +import com.google.gson.Gson; +import io.constructor.client.models.SearchabilityV2; +import java.util.ArrayList; +import java.util.Arrays; +import org.json.JSONObject; +import org.junit.AfterClass; +import org.junit.Test; + +public class ConstructorIOSearchabilityV2Test { + + private static String token = System.getenv("TEST_API_TOKEN"); + private static String apiKey = System.getenv("TEST_CATALOG_API_KEY"); + private static ArrayList searchabilitiesToCleanup = new ArrayList<>(); + + private void addSearchabilityToCleanupArray(String name, String section) { + if (section == null) { + section = ConstructorIO.DEFAULT_SECTION; + } + searchabilitiesToCleanup.add(name + "|" + section); + } + + private void addSearchabilityToCleanupArray(String name) { + addSearchabilityToCleanupArray(name, ConstructorIO.DEFAULT_SECTION); + } + + @AfterClass + public static void cleanupSearchabilities() throws ConstructorException { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + for (String searchability : searchabilitiesToCleanup) { + String[] parts = searchability.split("\\|"); + String name = parts[0]; + String section = parts[1]; + + try { + constructor.deleteSearchabilityV2(name, section); + } catch (ConstructorException e) { + System.err.println("Warning: Failed to clean up searchability: " + name); + } + } + } + + @Test + public void testRetrieveSearchabilitiesV2() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Retrieve all searchabilities + String retrieveResponse = constructor.retrieveSearchabilitiesV2(); + JSONObject jsonObj = new JSONObject(retrieveResponse); + + assertTrue("Response should have searchabilities array", jsonObj.has("searchabilities")); + assertTrue("Response should have total_count", jsonObj.has("total_count")); + } + + @Test + public void testRetrieveSearchabilitiesV2WithFilters() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + SearchabilitiesV2GetRequest request = + new SearchabilitiesV2GetRequest(ConstructorIO.DEFAULT_SECTION); + request.setPage(1); + request.setNumResultsPerPage(10); + + String retrieveResponse = constructor.retrieveSearchabilitiesV2(request); + JSONObject jsonObj = new JSONObject(retrieveResponse); + + assertTrue("Response should have searchabilities array", jsonObj.has("searchabilities")); + } + + @Test + public void testCreateOrUpdateSearchabilityV2() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + String string = Utils.getTestResource("searchability.v2.json"); + SearchabilityV2 searchability = new Gson().fromJson(string, SearchabilityV2.class); + searchability.setName("testSearchabilityV2"); + + SearchabilityV2Request request = + new SearchabilityV2Request( + searchability, "testSearchabilityV2", ConstructorIO.DEFAULT_SECTION); + + String response = constructor.createOrUpdateSearchabilityV2(request); + JSONObject jsonObj = new JSONObject(response); + + assertEquals("testSearchabilityV2", jsonObj.get("name")); + assertEquals(true, jsonObj.get("fuzzy_searchable")); + assertEquals(false, jsonObj.get("exact_searchable")); + assertEquals(true, jsonObj.get("displayable")); + addSearchabilityToCleanupArray("testSearchabilityV2"); + } + + @Test + public void testCreateOrUpdateSearchabilityV2WithSkipRebuild() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + String string = Utils.getTestResource("searchability.v2.json"); + SearchabilityV2 searchability = new Gson().fromJson(string, SearchabilityV2.class); + searchability.setName("testSearchabilityV2SkipRebuild"); + + SearchabilityV2Request request = + new SearchabilityV2Request( + searchability, + "testSearchabilityV2SkipRebuild", + ConstructorIO.DEFAULT_SECTION); + request.setSkipRebuild(true); + + String response = constructor.createOrUpdateSearchabilityV2(request); + JSONObject jsonObj = new JSONObject(response); + + assertEquals("testSearchabilityV2SkipRebuild", jsonObj.get("name")); + addSearchabilityToCleanupArray("testSearchabilityV2SkipRebuild"); + } + + @Test + public void testRetrieveSearchabilityV2() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create a searchability first + String string = Utils.getTestResource("searchability.v2.json"); + SearchabilityV2 searchability = new Gson().fromJson(string, SearchabilityV2.class); + searchability.setName("testRetrieveSearchabilityV2"); + + SearchabilityV2Request createRequest = + new SearchabilityV2Request( + searchability, + "testRetrieveSearchabilityV2", + ConstructorIO.DEFAULT_SECTION); + constructor.createOrUpdateSearchabilityV2(createRequest); + + // Retrieve the searchability + String retrieveResponse = + constructor.retrieveSearchabilityV2( + "testRetrieveSearchabilityV2", ConstructorIO.DEFAULT_SECTION); + JSONObject jsonObj = new JSONObject(retrieveResponse); + + assertEquals("testRetrieveSearchabilityV2", jsonObj.get("name")); + addSearchabilityToCleanupArray("testRetrieveSearchabilityV2"); + } + + @Test + public void testRetrieveSearchabilityV2WithDefaultSection() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create a searchability first + String string = Utils.getTestResource("searchability.v2.json"); + SearchabilityV2 searchability = new Gson().fromJson(string, SearchabilityV2.class); + searchability.setName("testRetrieveSearchabilityV2Default"); + + SearchabilityV2Request createRequest = + new SearchabilityV2Request( + searchability, + "testRetrieveSearchabilityV2Default", + ConstructorIO.DEFAULT_SECTION); + constructor.createOrUpdateSearchabilityV2(createRequest); + + // Retrieve the searchability with default section + String retrieveResponse = + constructor.retrieveSearchabilityV2("testRetrieveSearchabilityV2Default"); + JSONObject jsonObj = new JSONObject(retrieveResponse); + + assertEquals("testRetrieveSearchabilityV2Default", jsonObj.get("name")); + addSearchabilityToCleanupArray("testRetrieveSearchabilityV2Default"); + } + + @Test + public void testDeleteSearchabilityV2() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create a searchability first + String string = Utils.getTestResource("searchability.v2.json"); + SearchabilityV2 searchability = new Gson().fromJson(string, SearchabilityV2.class); + searchability.setName("testDeleteSearchabilityV2"); + + SearchabilityV2Request createRequest = + new SearchabilityV2Request( + searchability, "testDeleteSearchabilityV2", ConstructorIO.DEFAULT_SECTION); + constructor.createOrUpdateSearchabilityV2(createRequest); + + // Delete the searchability + String deleteResponse = + constructor.deleteSearchabilityV2( + "testDeleteSearchabilityV2", ConstructorIO.DEFAULT_SECTION); + JSONObject jsonObj = new JSONObject(deleteResponse); + + assertEquals("testDeleteSearchabilityV2", jsonObj.get("name")); + } + + @Test + public void testDeleteSearchabilityV2WithDefaultSection() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create a searchability first + String string = Utils.getTestResource("searchability.v2.json"); + SearchabilityV2 searchability = new Gson().fromJson(string, SearchabilityV2.class); + searchability.setName("testDeleteSearchabilityV2Default"); + + SearchabilityV2Request createRequest = + new SearchabilityV2Request( + searchability, + "testDeleteSearchabilityV2Default", + ConstructorIO.DEFAULT_SECTION); + constructor.createOrUpdateSearchabilityV2(createRequest); + + // Delete the searchability with default section + String deleteResponse = + constructor.deleteSearchabilityV2("testDeleteSearchabilityV2Default"); + JSONObject jsonObj = new JSONObject(deleteResponse); + + assertEquals("testDeleteSearchabilityV2Default", jsonObj.get("name")); + } + + @Test + public void testDeleteSearchabilityV2WithRequest() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create a searchability first + String string = Utils.getTestResource("searchability.v2.json"); + SearchabilityV2 searchability = new Gson().fromJson(string, SearchabilityV2.class); + searchability.setName("testDeleteSearchabilityV2Request"); + + SearchabilityV2Request createRequest = + new SearchabilityV2Request( + searchability, + "testDeleteSearchabilityV2Request", + ConstructorIO.DEFAULT_SECTION); + constructor.createOrUpdateSearchabilityV2(createRequest); + + // Delete the searchability using request object + SearchabilityV2Request deleteRequest = + new SearchabilityV2Request( + "testDeleteSearchabilityV2Request", ConstructorIO.DEFAULT_SECTION); + String deleteResponse = constructor.deleteSearchabilityV2(deleteRequest); + JSONObject jsonObj = new JSONObject(deleteResponse); + + assertEquals("testDeleteSearchabilityV2Request", jsonObj.get("name")); + } + + @Test + public void testCreateOrUpdateSearchabilitiesV2Bulk() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + String string = Utils.getTestResource("searchability.v2.json"); + SearchabilityV2 searchability1 = new Gson().fromJson(string, SearchabilityV2.class); + searchability1.setName("testBulkSearchabilityV2_1"); + + SearchabilityV2 searchability2 = new Gson().fromJson(string, SearchabilityV2.class); + searchability2.setName("testBulkSearchabilityV2_2"); + searchability2.setFuzzySearchable(false); + searchability2.setExactSearchable(true); + + SearchabilitiesV2Request bulkRequest = + new SearchabilitiesV2Request( + Arrays.asList(searchability1, searchability2), + ConstructorIO.DEFAULT_SECTION); + + String response = constructor.createOrUpdateSearchabilitiesV2(bulkRequest); + JSONObject jsonObj = new JSONObject(response); + + assertTrue("Response should have searchabilities array", jsonObj.has("searchabilities")); + addSearchabilityToCleanupArray("testBulkSearchabilityV2_1"); + addSearchabilityToCleanupArray("testBulkSearchabilityV2_2"); + } + + @Test + public void testDeleteSearchabilitiesV2Bulk() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + + // Create searchabilities first + String string = Utils.getTestResource("searchability.v2.json"); + SearchabilityV2 searchability1 = new Gson().fromJson(string, SearchabilityV2.class); + searchability1.setName("testBulkDeleteSearchabilityV2_1"); + + SearchabilityV2 searchability2 = new Gson().fromJson(string, SearchabilityV2.class); + searchability2.setName("testBulkDeleteSearchabilityV2_2"); + + SearchabilitiesV2Request createRequest = + new SearchabilitiesV2Request( + Arrays.asList(searchability1, searchability2), + ConstructorIO.DEFAULT_SECTION); + constructor.createOrUpdateSearchabilitiesV2(createRequest); + + // Delete searchabilities + SearchabilitiesV2DeleteRequest deleteRequest = + new SearchabilitiesV2DeleteRequest( + Arrays.asList( + "testBulkDeleteSearchabilityV2_1", + "testBulkDeleteSearchabilityV2_2"), + ConstructorIO.DEFAULT_SECTION); + + String response = constructor.deleteSearchabilitiesV2(deleteRequest); + JSONObject jsonObj = new JSONObject(response); + + assertTrue("Response should have searchabilities array", jsonObj.has("searchabilities")); + } + + @Test(expected = ConstructorException.class) + public void testDeleteNonExistentSearchabilityV2ThrowsException() throws Exception { + ConstructorIO constructor = new ConstructorIO(token, apiKey, true, null); + constructor.deleteSearchabilityV2( + "nonExistentSearchabilityV2", ConstructorIO.DEFAULT_SECTION); + } + + @Test(expected = IllegalArgumentException.class) + public void testSearchabilityV2RequestWithNullNameThrowsException() { + new SearchabilityV2Request((String) null, ConstructorIO.DEFAULT_SECTION); + } + + @Test(expected = IllegalArgumentException.class) + public void testSearchabilityV2RequestWithEmptyNameThrowsException() { + new SearchabilityV2Request(" ", ConstructorIO.DEFAULT_SECTION); + } + + @Test + public void testSearchabilityV2DefaultValues() { + SearchabilityV2 searchability = new SearchabilityV2(); + assertNull("Name should default to null", searchability.getName()); + assertNull("Fuzzy searchable should default to null", searchability.getFuzzySearchable()); + assertNull("Exact searchable should default to null", searchability.getExactSearchable()); + assertNull("Displayable should default to null", searchability.getDisplayable()); + assertNull("Hidden should default to null", searchability.getHidden()); + } +} diff --git a/constructorio-client/src/test/java/io/constructor/client/FacetConfigurationV2Test.java b/constructorio-client/src/test/java/io/constructor/client/FacetConfigurationV2Test.java new file mode 100644 index 00000000..006dd50e --- /dev/null +++ b/constructorio-client/src/test/java/io/constructor/client/FacetConfigurationV2Test.java @@ -0,0 +1,181 @@ +package io.constructor.client; + +import static org.junit.Assert.*; + +import com.google.gson.Gson; +import io.constructor.client.models.FacetConfigurationV2; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import org.junit.Test; + +public class FacetConfigurationV2Test { + + @Test + public void testDeserializeFacetConfigurationV2FromJson() throws Exception { + String string = Utils.getTestResource("facet.configuration.v2.json"); + FacetConfigurationV2 facetConfiguration = + new Gson().fromJson(string, FacetConfigurationV2.class); + + assertEquals("brand", facetConfiguration.getName()); + assertEquals("brand", facetConfiguration.getPathInMetadata()); + assertEquals("multiple", facetConfiguration.getType()); + assertEquals("Brand", facetConfiguration.getDisplayName()); + assertEquals("relevance", facetConfiguration.getSortOrder()); + assertEquals(true, facetConfiguration.getSortDescending()); + assertEquals("any", facetConfiguration.getMatchType()); + assertEquals(Integer.valueOf(2), facetConfiguration.getPosition()); + assertEquals(false, facetConfiguration.getHidden()); + assertEquals(false, facetConfiguration.getIsProtected()); + assertEquals(true, facetConfiguration.getCountable()); + assertEquals(Integer.valueOf(300), facetConfiguration.getOptionsLimit()); + assertNotNull(facetConfiguration.getData()); + assertEquals("bar", facetConfiguration.getData().get("foo")); + } + + @Test + public void testSerializeFacetConfigurationV2ToJson() { + FacetConfigurationV2 facetConfiguration = new FacetConfigurationV2(); + facetConfiguration.setName("testFacet"); + facetConfiguration.setPathInMetadata("testFacet"); + facetConfiguration.setType("multiple"); + facetConfiguration.setDisplayName("Test Facet"); + facetConfiguration.setSortOrder("relevance"); + facetConfiguration.setSortDescending(true); + facetConfiguration.setMatchType("any"); + facetConfiguration.setPosition(1); + facetConfiguration.setHidden(false); + facetConfiguration.setIsProtected(false); + facetConfiguration.setCountable(true); + facetConfiguration.setOptionsLimit(500); + + Map data = new HashMap<>(); + data.put("key", "value"); + facetConfiguration.setData(data); + + String json = new Gson().toJson(facetConfiguration); + + assertTrue(json.contains("\"name\":\"testFacet\"")); + assertTrue(json.contains("\"path_in_metadata\":\"testFacet\"")); + assertTrue(json.contains("\"type\":\"multiple\"")); + assertTrue(json.contains("\"display_name\":\"Test Facet\"")); + assertTrue(json.contains("\"sort_order\":\"relevance\"")); + assertTrue(json.contains("\"sort_descending\":true")); + assertTrue(json.contains("\"match_type\":\"any\"")); + assertTrue(json.contains("\"position\":1")); + assertTrue(json.contains("\"hidden\":false")); + assertTrue(json.contains("\"protected\":false")); + assertTrue(json.contains("\"countable\":true")); + assertTrue(json.contains("\"options_limit\":500")); + } + + @Test + public void testFacetConfigurationV2WithRangeType() { + FacetConfigurationV2 facetConfiguration = new FacetConfigurationV2(); + facetConfiguration.setName("priceFacet"); + facetConfiguration.setPathInMetadata("price"); + facetConfiguration.setType("range"); + facetConfiguration.setRangeType("static"); + facetConfiguration.setRangeFormat("boundaries"); + facetConfiguration.setRangeLimits(Arrays.asList(10, 25, 50, 100)); + + assertEquals("range", facetConfiguration.getType()); + assertEquals("static", facetConfiguration.getRangeType()); + assertEquals("boundaries", facetConfiguration.getRangeFormat()); + assertEquals(4, facetConfiguration.getRangeLimits().size()); + } + + @Test + public void testFacetConfigurationV2WithRangeInclusive() { + FacetConfigurationV2 facetConfiguration = new FacetConfigurationV2(); + facetConfiguration.setRangeInclusive("above"); + + assertEquals("above", facetConfiguration.getRangeInclusive()); + } + + @Test + public void testFacetConfigurationV2Timestamps() { + FacetConfigurationV2 facetConfiguration = new FacetConfigurationV2(); + facetConfiguration.setCreatedAt("2024-01-15T10:30:00Z"); + facetConfiguration.setUpdatedAt("2024-01-16T14:45:00Z"); + + assertEquals("2024-01-15T10:30:00Z", facetConfiguration.getCreatedAt()); + assertEquals("2024-01-16T14:45:00Z", facetConfiguration.getUpdatedAt()); + } + + @Test + public void testFacetConfigurationV2Request() { + FacetConfigurationV2 config = new FacetConfigurationV2(); + config.setName("testFacet"); + config.setPathInMetadata("testFacet"); + + FacetConfigurationV2Request request = new FacetConfigurationV2Request(config, "Products"); + + assertEquals(config, request.getFacetConfiguration()); + assertEquals("Products", request.getSection()); + } + + @Test + public void testFacetConfigurationV2RequestWithDefaultSection() { + FacetConfigurationV2 config = new FacetConfigurationV2(); + config.setName("testFacet"); + config.setPathInMetadata("testFacet"); + + FacetConfigurationV2Request request = new FacetConfigurationV2Request(config); + + assertEquals(config, request.getFacetConfiguration()); + assertEquals("Products", request.getSection()); + } + + @Test(expected = IllegalArgumentException.class) + public void testFacetConfigurationV2RequestWithNullConfig() { + new FacetConfigurationV2Request(null, "Products"); + } + + @Test(expected = IllegalArgumentException.class) + public void testFacetConfigurationV2RequestWithNullSection() { + FacetConfigurationV2 config = new FacetConfigurationV2(); + config.setName("testFacet"); + new FacetConfigurationV2Request(config, null); + } + + @Test + public void testFacetConfigurationsV2Request() { + FacetConfigurationV2 config1 = new FacetConfigurationV2(); + config1.setName("facet1"); + config1.setPathInMetadata("facet1"); + + FacetConfigurationV2 config2 = new FacetConfigurationV2(); + config2.setName("facet2"); + config2.setPathInMetadata("facet2"); + + FacetConfigurationsV2Request request = + new FacetConfigurationsV2Request(Arrays.asList(config1, config2), "Products"); + + assertEquals(2, request.getFacetConfigurations().size()); + assertEquals("Products", request.getSection()); + } + + @Test + public void testFacetConfigurationsV2RequestWithDefaultSection() { + FacetConfigurationV2 config = new FacetConfigurationV2(); + config.setName("facet1"); + config.setPathInMetadata("facet1"); + + FacetConfigurationsV2Request request = + new FacetConfigurationsV2Request(Arrays.asList(config)); + + assertEquals(1, request.getFacetConfigurations().size()); + assertEquals("Products", request.getSection()); + } + + @Test(expected = IllegalArgumentException.class) + public void testFacetConfigurationsV2RequestWithEmptyList() { + new FacetConfigurationsV2Request(Arrays.asList(), "Products"); + } + + @Test(expected = IllegalArgumentException.class) + public void testFacetConfigurationsV2RequestWithNullList() { + new FacetConfigurationsV2Request(null, "Products"); + } +} diff --git a/constructorio-client/src/test/java/io/constructor/client/SearchabilityV2Test.java b/constructorio-client/src/test/java/io/constructor/client/SearchabilityV2Test.java new file mode 100644 index 00000000..3ea3fa02 --- /dev/null +++ b/constructorio-client/src/test/java/io/constructor/client/SearchabilityV2Test.java @@ -0,0 +1,264 @@ +package io.constructor.client; + +import static org.junit.Assert.*; + +import com.google.gson.Gson; +import io.constructor.client.models.SearchabilityV2; +import java.util.Arrays; +import org.junit.Test; + +public class SearchabilityV2Test { + + @Test + public void testDeserializeSearchabilityV2FromJson() throws Exception { + String string = Utils.getTestResource("searchability.v2.json"); + SearchabilityV2 searchability = new Gson().fromJson(string, SearchabilityV2.class); + + assertEquals("title", searchability.getName()); + assertEquals(true, searchability.getFuzzySearchable()); + assertEquals(false, searchability.getExactSearchable()); + assertEquals(true, searchability.getDisplayable()); + assertEquals(false, searchability.getHidden()); + } + + @Test + public void testSerializeSearchabilityV2ToJson() { + SearchabilityV2 searchability = new SearchabilityV2(); + searchability.setName("description"); + searchability.setFuzzySearchable(false); + searchability.setExactSearchable(true); + searchability.setDisplayable(true); + searchability.setHidden(false); + + String json = new Gson().toJson(searchability); + + assertTrue(json.contains("\"name\":\"description\"")); + assertTrue(json.contains("\"fuzzy_searchable\":false")); + assertTrue(json.contains("\"exact_searchable\":true")); + assertTrue(json.contains("\"displayable\":true")); + assertTrue(json.contains("\"hidden\":false")); + } + + @Test + public void testSearchabilityV2Timestamps() { + SearchabilityV2 searchability = new SearchabilityV2(); + searchability.setCreatedAt("2024-01-15T10:30:00Z"); + searchability.setUpdatedAt("2024-01-16T14:45:00Z"); + + assertEquals("2024-01-15T10:30:00Z", searchability.getCreatedAt()); + assertEquals("2024-01-16T14:45:00Z", searchability.getUpdatedAt()); + } + + @Test + public void testSearchabilityV2Request() { + SearchabilityV2 config = new SearchabilityV2(); + config.setName("title"); + config.setFuzzySearchable(true); + + SearchabilityV2Request request = + new SearchabilityV2Request(config, "title", ConstructorIO.DEFAULT_SECTION); + + assertEquals(config, request.getSearchability()); + assertEquals("title", request.getName()); + assertEquals(ConstructorIO.DEFAULT_SECTION, request.getSection()); + } + + @Test + public void testSearchabilityV2RequestWithDefaultSection() { + SearchabilityV2 config = new SearchabilityV2(); + config.setName("title"); + + SearchabilityV2Request request = new SearchabilityV2Request(config, "title"); + + assertEquals(config, request.getSearchability()); + assertEquals("title", request.getName()); + assertEquals(ConstructorIO.DEFAULT_SECTION, request.getSection()); + } + + @Test + public void testSearchabilityV2RequestForReadOperations() { + SearchabilityV2Request request = + new SearchabilityV2Request("title", ConstructorIO.DEFAULT_SECTION); + + assertNull(request.getSearchability()); + assertEquals("title", request.getName()); + assertEquals(ConstructorIO.DEFAULT_SECTION, request.getSection()); + } + + @Test + public void testSearchabilityV2RequestForReadWithDefaultSection() { + SearchabilityV2Request request = new SearchabilityV2Request("title"); + + assertNull(request.getSearchability()); + assertEquals("title", request.getName()); + assertEquals(ConstructorIO.DEFAULT_SECTION, request.getSection()); + } + + @Test + public void testSearchabilityV2RequestWithSkipRebuild() { + SearchabilityV2Request request = new SearchabilityV2Request("title"); + request.setSkipRebuild(true); + + assertEquals(true, request.getSkipRebuild()); + } + + @Test(expected = IllegalArgumentException.class) + public void testSearchabilityV2RequestWithNullNameThrowsException() { + new SearchabilityV2Request((String) null, ConstructorIO.DEFAULT_SECTION); + } + + @Test(expected = IllegalArgumentException.class) + public void testSearchabilityV2RequestWithEmptyNameThrowsException() { + new SearchabilityV2Request(" ", ConstructorIO.DEFAULT_SECTION); + } + + @Test(expected = IllegalArgumentException.class) + public void testSearchabilityV2RequestWithNullSectionThrowsException() { + SearchabilityV2 config = new SearchabilityV2(); + config.setName("title"); + new SearchabilityV2Request(config, "title", null); + } + + @Test + public void testSearchabilitiesV2Request() { + SearchabilityV2 config1 = new SearchabilityV2(); + config1.setName("title"); + config1.setFuzzySearchable(true); + + SearchabilityV2 config2 = new SearchabilityV2(); + config2.setName("description"); + config2.setExactSearchable(true); + + SearchabilitiesV2Request request = + new SearchabilitiesV2Request( + Arrays.asList(config1, config2), ConstructorIO.DEFAULT_SECTION); + + assertEquals(2, request.getSearchabilities().size()); + assertEquals(ConstructorIO.DEFAULT_SECTION, request.getSection()); + } + + @Test + public void testSearchabilitiesV2RequestWithDefaultSection() { + SearchabilityV2 config = new SearchabilityV2(); + config.setName("title"); + + SearchabilitiesV2Request request = new SearchabilitiesV2Request(Arrays.asList(config)); + + assertEquals(1, request.getSearchabilities().size()); + assertEquals(ConstructorIO.DEFAULT_SECTION, request.getSection()); + } + + @Test + public void testSearchabilitiesV2RequestWithSkipRebuild() { + SearchabilityV2 config = new SearchabilityV2(); + config.setName("title"); + + SearchabilitiesV2Request request = new SearchabilitiesV2Request(Arrays.asList(config)); + request.setSkipRebuild(true); + + assertEquals(true, request.getSkipRebuild()); + } + + @Test(expected = IllegalArgumentException.class) + public void testSearchabilitiesV2RequestWithNullListThrowsException() { + new SearchabilitiesV2Request(null, ConstructorIO.DEFAULT_SECTION); + } + + @Test(expected = IllegalArgumentException.class) + public void testSearchabilitiesV2RequestWithEmptyListThrowsException() { + new SearchabilitiesV2Request( + Arrays.asList(), ConstructorIO.DEFAULT_SECTION); + } + + @Test + public void testSearchabilitiesV2GetRequest() { + SearchabilitiesV2GetRequest request = + new SearchabilitiesV2GetRequest(ConstructorIO.DEFAULT_SECTION); + + assertEquals(ConstructorIO.DEFAULT_SECTION, request.getSection()); + } + + @Test + public void testSearchabilitiesV2GetRequestWithDefaultSection() { + SearchabilitiesV2GetRequest request = new SearchabilitiesV2GetRequest(); + + assertEquals(ConstructorIO.DEFAULT_SECTION, request.getSection()); + } + + @Test + public void testSearchabilitiesV2GetRequestWithPagination() { + SearchabilitiesV2GetRequest request = new SearchabilitiesV2GetRequest(); + request.setPage(2); + request.setNumResultsPerPage(50); + request.setOffset(100); + + assertEquals(Integer.valueOf(2), request.getPage()); + assertEquals(Integer.valueOf(50), request.getNumResultsPerPage()); + assertEquals(Integer.valueOf(100), request.getOffset()); + } + + @Test + public void testSearchabilitiesV2GetRequestWithFilters() { + SearchabilitiesV2GetRequest request = new SearchabilitiesV2GetRequest(); + request.setName("title*"); + request.setFuzzySearchable(true); + request.setExactSearchable(false); + request.setDisplayable(true); + request.setMatchType("and"); + request.setSortBy("name"); + request.setSortOrder("ascending"); + + assertEquals("title*", request.getName()); + assertEquals(true, request.getFuzzySearchable()); + assertEquals(false, request.getExactSearchable()); + assertEquals(true, request.getDisplayable()); + assertEquals("and", request.getMatchType()); + assertEquals("name", request.getSortBy()); + assertEquals("ascending", request.getSortOrder()); + } + + @Test(expected = IllegalArgumentException.class) + public void testSearchabilitiesV2GetRequestWithNullSectionThrowsException() { + new SearchabilitiesV2GetRequest(null); + } + + @Test + public void testSearchabilitiesV2DeleteRequest() { + SearchabilitiesV2DeleteRequest request = + new SearchabilitiesV2DeleteRequest( + Arrays.asList("title", "description"), ConstructorIO.DEFAULT_SECTION); + + assertEquals(2, request.getSearchabilityNames().size()); + assertTrue(request.getSearchabilityNames().contains("title")); + assertTrue(request.getSearchabilityNames().contains("description")); + assertEquals(ConstructorIO.DEFAULT_SECTION, request.getSection()); + } + + @Test + public void testSearchabilitiesV2DeleteRequestWithDefaultSection() { + SearchabilitiesV2DeleteRequest request = + new SearchabilitiesV2DeleteRequest(Arrays.asList("title")); + + assertEquals(1, request.getSearchabilityNames().size()); + assertEquals(ConstructorIO.DEFAULT_SECTION, request.getSection()); + } + + @Test + public void testSearchabilitiesV2DeleteRequestWithSkipRebuild() { + SearchabilitiesV2DeleteRequest request = + new SearchabilitiesV2DeleteRequest(Arrays.asList("title")); + request.setSkipRebuild(true); + + assertEquals(true, request.getSkipRebuild()); + } + + @Test(expected = IllegalArgumentException.class) + public void testSearchabilitiesV2DeleteRequestWithNullNamesThrowsException() { + new SearchabilitiesV2DeleteRequest(null, ConstructorIO.DEFAULT_SECTION); + } + + @Test(expected = IllegalArgumentException.class) + public void testSearchabilitiesV2DeleteRequestWithEmptyNamesThrowsException() { + new SearchabilitiesV2DeleteRequest(Arrays.asList(), ConstructorIO.DEFAULT_SECTION); + } +} diff --git a/constructorio-client/src/test/resources/facet.configuration.v2.json b/constructorio-client/src/test/resources/facet.configuration.v2.json new file mode 100644 index 00000000..13deff15 --- /dev/null +++ b/constructorio-client/src/test/resources/facet.configuration.v2.json @@ -0,0 +1,17 @@ +{ + "name": "brand", + "path_in_metadata": "brand", + "type": "multiple", + "display_name": "Brand", + "sort_order": "relevance", + "sort_descending": true, + "match_type": "any", + "position": 2, + "hidden": false, + "protected": false, + "countable": true, + "options_limit": 300, + "data": { + "foo": "bar" + } +} diff --git a/constructorio-client/src/test/resources/searchability.v2.json b/constructorio-client/src/test/resources/searchability.v2.json new file mode 100644 index 00000000..46265bbd --- /dev/null +++ b/constructorio-client/src/test/resources/searchability.v2.json @@ -0,0 +1,7 @@ +{ + "name": "title", + "fuzzy_searchable": true, + "exact_searchable": false, + "displayable": true, + "hidden": false +}