diff --git a/retail/snippets/README.md b/retail/snippets/README.md new file mode 100644 index 00000000000..9c44aecbfae --- /dev/null +++ b/retail/snippets/README.md @@ -0,0 +1,26 @@ +# Vertex AI Search for commerce Samples + +This directory contains Java samples for [Vertex AI Search for commerce](https://cloud.google.com/retail/docs/search-basic#search). + +## Prerequisites + +To run these samples, you must have: + +1. **A Google Cloud Project** with the [Vertex AI Search for commerce API](https://console.cloud.google.com/apis/library/retail.googleapis.com) enabled. +2. **Vertex AI Search for commerce** set up with a valid catalog and serving configuration (placement). +3. **Authentication**: These samples use [Application Default Credentials (ADC)](https://cloud.google.com/docs/authentication/provide-credentials-adc). + - If running locally, you can set up ADC by running: + ```bash + gcloud auth application-default login + ``` +4. **IAM Roles**: The service account or user running the samples needs the `roles/retail.viewer` (Retail Viewer) role or higher. + +## Samples + +- **[Search.java](src/main/java/com/example/search/Search.java)**: Basic search request showing both text search and browse search (using categories). +- **[SearchPagination.java](src/main/java/com/example/search/SearchPagination.java)**: Shows how to use `next_page_token` to paginate through search results. +- **[SearchOffset.java](src/main/java/com/example/search/SearchOffset.java)**: Shows how to use `offset` to skip a specified number of results. + +## Documentation + +For more information, see the [Vertex AI Search for commerce documentation](https://docs.cloud.google.com/retail/docs/search-basic#search). \ No newline at end of file diff --git a/retail/snippets/pom.xml b/retail/snippets/pom.xml new file mode 100644 index 00000000000..0066e78fb56 --- /dev/null +++ b/retail/snippets/pom.xml @@ -0,0 +1,72 @@ + + + + 4.0.0 + com.example.retail + retail-samples + 1.0-SNAPSHOT + + + + shared-configuration + com.google.cloud.samples + 1.2.2 + + + + 21 + 21 + + + + + + libraries-bom + com.google.cloud + import + pom + 26.80.0 + + + + + + + com.google.cloud + google-cloud-retail + + + + + truth + com.google.truth + test + 1.4.5 + + + org.junit.jupiter + junit-jupiter + 5.14.3 + test + + + diff --git a/retail/snippets/src/main/java/com/example/search/Search.java b/retail/snippets/src/main/java/com/example/search/Search.java new file mode 100644 index 00000000000..587eabc2da2 --- /dev/null +++ b/retail/snippets/src/main/java/com/example/search/Search.java @@ -0,0 +1,72 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.search; + +// [START retail_v2_search_request] + +import com.google.cloud.retail.v2.BranchName; +import com.google.cloud.retail.v2.Product; +import com.google.cloud.retail.v2.SearchRequest; +import com.google.cloud.retail.v2.SearchResponse; +import com.google.cloud.retail.v2.SearchResponse.SearchResult; +import com.google.cloud.retail.v2.SearchServiceClient; +import com.google.cloud.retail.v2.SearchServiceClient.SearchPagedResponse; +import com.google.cloud.retail.v2.ServingConfigName; +import java.io.IOException; +import java.util.List; + +public class Search { + public static void main(String[] args) throws IOException { + String projectId = "my-project-id"; + String visitorId = "my-visitor-id"; + String query = "my search query"; + List categories = List.of("category"); + + search(projectId, visitorId, query, categories); + } + + public static void search( + String projectId, String visitorId, String query, List categories) + throws IOException { + try (SearchServiceClient searchServiceClient = SearchServiceClient.create()) { + ServingConfigName servingConfigName = + ServingConfigName.of(projectId, "global", "default_catalog", "default_search"); + BranchName branchName = + BranchName.of(projectId, "global", "default_catalog", "default_branch"); + SearchRequest searchRequest = + SearchRequest.newBuilder() + .setPlacement(servingConfigName.toString()) + .setBranch(branchName.toString()) + .setVisitorId(visitorId) + .setQuery(query) + .addAllPageCategories(categories) + .setPageSize(10) + .build(); + SearchPagedResponse response = searchServiceClient.search(searchRequest); + + SearchResponse searchResponse = response.getPage().getResponse(); + + System.out.println("Found " + searchResponse.getResultsCount() + " results in current page"); + for (SearchResult searchResult : searchResponse.getResultsList()) { + Product product = searchResult.getProduct(); + System.out.println("---- Search Result ----"); + System.out.println("Product Name: " + product.getName()); + } + } + } +} +// [END retail_v2_search_request] diff --git a/retail/snippets/src/main/java/com/example/search/SearchOffset.java b/retail/snippets/src/main/java/com/example/search/SearchOffset.java new file mode 100644 index 00000000000..1a8edd681da --- /dev/null +++ b/retail/snippets/src/main/java/com/example/search/SearchOffset.java @@ -0,0 +1,70 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.search; + +// [START retail_v2_search_offset] + +import com.google.cloud.retail.v2.BranchName; +import com.google.cloud.retail.v2.Product; +import com.google.cloud.retail.v2.SearchRequest; +import com.google.cloud.retail.v2.SearchResponse; +import com.google.cloud.retail.v2.SearchResponse.SearchResult; +import com.google.cloud.retail.v2.SearchServiceClient; +import com.google.cloud.retail.v2.SearchServiceClient.SearchPagedResponse; +import com.google.cloud.retail.v2.ServingConfigName; +import java.io.IOException; + +public class SearchOffset { + public static void main(String[] args) throws IOException { + String projectId = "my-project-id"; + String visitorId = "my-visitor-id"; + String query = "my search query"; + int offset = 10; + + searchWithOffset(projectId, visitorId, query, offset); + } + + public static void searchWithOffset(String projectId, String visitorId, String query, int offset) + throws IOException { + try (SearchServiceClient searchServiceClient = SearchServiceClient.create()) { + ServingConfigName servingConfigName = + ServingConfigName.of(projectId, "global", "default_catalog", "default_search"); + BranchName branchName = + BranchName.of(projectId, "global", "default_catalog", "default_branch"); + SearchRequest searchRequest = + SearchRequest.newBuilder() + .setPlacement(servingConfigName.toString()) + .setBranch(branchName.toString()) + .setVisitorId(visitorId) + .setQuery(query) + .setPageSize(10) + .setOffset(offset) + .build(); + SearchPagedResponse response = searchServiceClient.search(searchRequest); + + SearchResponse searchResponse = response.getPage().getResponse(); + + System.out.println("Found " + searchResponse.getResultsCount() + " results in current page"); + for (SearchResult searchResult : searchResponse.getResultsList()) { + Product product = searchResult.getProduct(); + System.out.println("---- Search Result ----"); + System.out.println("Product Name: " + product.getName()); + } + } + } +} +// [END retail_v2_search_offset] diff --git a/retail/snippets/src/main/java/com/example/search/SearchPagination.java b/retail/snippets/src/main/java/com/example/search/SearchPagination.java new file mode 100644 index 00000000000..ec69a83fd04 --- /dev/null +++ b/retail/snippets/src/main/java/com/example/search/SearchPagination.java @@ -0,0 +1,81 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.search; + +// [START retail_v2_search_pagination] + +import com.google.cloud.retail.v2.BranchName; +import com.google.cloud.retail.v2.Product; +import com.google.cloud.retail.v2.SearchRequest; +import com.google.cloud.retail.v2.SearchResponse.SearchResult; +import com.google.cloud.retail.v2.SearchServiceClient; +import com.google.cloud.retail.v2.SearchServiceClient.SearchPage; +import com.google.cloud.retail.v2.SearchServiceClient.SearchPagedResponse; +import com.google.cloud.retail.v2.ServingConfigName; +import java.io.IOException; + +public class SearchPagination { + public static void main(String[] args) throws IOException { + String projectId = "my-project-id"; + String visitorId = "my-visitor-id"; + String query = "my search query"; + int pageSize = 10; + + searchWithPagination(projectId, visitorId, query, pageSize); + } + + public static void searchWithPagination( + String projectId, String visitorId, String query, int pageSize) throws IOException { + try (SearchServiceClient searchServiceClient = SearchServiceClient.create()) { + ServingConfigName servingConfigName = + ServingConfigName.of(projectId, "global", "default_catalog", "default_search"); + BranchName branchName = + BranchName.of(projectId, "global", "default_catalog", "default_branch"); + SearchRequest request = + SearchRequest.newBuilder() + .setPlacement(servingConfigName.toString()) + .setBranch(branchName.toString()) + .setVisitorId(visitorId) + .setQuery(query) + .setPageSize(pageSize) + .build(); + int currentPage = 0; + while (true) { + SearchPagedResponse response = searchServiceClient.search(request); + + SearchPage page = response.getPage(); + currentPage++; + System.out.println("\nResults of page number " + currentPage + ":"); + System.out.println( + "Found " + page.getResponse().getResultsCount() + " results in current page"); + for (SearchResult searchResult : page.getResponse().getResultsList()) { + Product product = searchResult.getProduct(); + System.out.println("---- Search Result ----"); + System.out.println("Product Name: " + product.getName()); + } + + if (page.hasNextPage()) { + request = request.toBuilder().setPageToken(page.getNextPageToken()).build(); + } else { + System.out.println("\nNo more available pages."); + break; + } + } + } + } +} +// [END retail_v2_search_pagination] diff --git a/retail/snippets/src/test/java/com/example/search/SearchIT.java b/retail/snippets/src/test/java/com/example/search/SearchIT.java new file mode 100644 index 00000000000..d5c88d71c35 --- /dev/null +++ b/retail/snippets/src/test/java/com/example/search/SearchIT.java @@ -0,0 +1,92 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.search; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.google.cloud.retail.v2.Product; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.List; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class SearchIT { + private static PrintStream origPrintStream; + private static ByteArrayOutputStream bout; + + private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); + private static final String VISITOR_ID = "test_visitor"; + private static final int NUM_PRODUCTS_TO_TEST = 2; + private static final String PRODUCT_TITLE = "Hot Java Testing"; + private static final List CATEGORIES = List.of("Beverages"); + private static List productsToTest; + + private static void requireEnvVar(String varName) { + assertNotNull( + "Environment variable " + varName + " is required to perform these tests.", + System.getenv(varName)); + } + + @BeforeAll + public static void setUp() throws InterruptedException, IOException { + requireEnvVar("GOOGLE_CLOUD_PROJECT"); + + // Create products to be searched + productsToTest = Utils.createProductsToTest(NUM_PRODUCTS_TO_TEST, PROJECT_ID, PRODUCT_TITLE); + Utils.waitForProductsToBeIndexed(PROJECT_ID, productsToTest, PRODUCT_TITLE); + + origPrintStream = System.out; + bout = new ByteArrayOutputStream(); + System.setOut(new PrintStream(bout)); + } + + @AfterAll + public static void tearDown() throws IOException { + // Clean up products created for testing + for (Product product : productsToTest) { + Utils.deleteProduct(product.getName()); + } + + System.setOut(origPrintStream); + } + + @Test + public void testSearch() throws Exception { + Search.search(PROJECT_ID, VISITOR_ID, PRODUCT_TITLE, CATEGORIES); + String output = bout.toString(); + assertThat(output).contains("Found " + NUM_PRODUCTS_TO_TEST + " results in current page"); + assertThat(output).contains("Product Name:"); + } + + @Test + public void testSearchWithoutQueryString() throws Exception { + Search.search(PROJECT_ID, VISITOR_ID, "", CATEGORIES); + String output = bout.toString(); + assertThat(output).contains("Product Name:"); + } + + @Test + public void testSearch_productsNotFound() throws Exception { + Search.search(PROJECT_ID, VISITOR_ID, "INVALID_QUERY_VALUE_FOR_TEST", CATEGORIES); + String output = bout.toString(); + assertThat(output).contains("Found " + 0 + " results in current page"); + } +} diff --git a/retail/snippets/src/test/java/com/example/search/SearchOffsetIT.java b/retail/snippets/src/test/java/com/example/search/SearchOffsetIT.java new file mode 100644 index 00000000000..06d39c9793c --- /dev/null +++ b/retail/snippets/src/test/java/com/example/search/SearchOffsetIT.java @@ -0,0 +1,107 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.search; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.google.api.gax.rpc.InvalidArgumentException; +import com.google.cloud.retail.v2.Product; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.List; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class SearchOffsetIT { + private static PrintStream origPrintStream; + private static ByteArrayOutputStream bout; + + private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); + private static final String VISITOR_ID = "test_visitor"; + private static final int NUM_PRODUCTS_TO_TEST = 2; + private static final String PRODUCT_TITLE = "Hot Java Testing"; + private static List productsToTest; + + private static void requireEnvVar(String varName) { + assertNotNull( + "Environment variable " + varName + " is required to perform these tests.", + System.getenv(varName)); + } + + @BeforeAll + public static void setUp() throws InterruptedException, IOException { + requireEnvVar("GOOGLE_CLOUD_PROJECT"); + + // Create products to be searched + productsToTest = Utils.createProductsToTest(NUM_PRODUCTS_TO_TEST, PROJECT_ID, PRODUCT_TITLE); + Utils.waitForProductsToBeIndexed(PROJECT_ID, productsToTest, PRODUCT_TITLE); + + origPrintStream = System.out; + bout = new ByteArrayOutputStream(); + System.setOut(new PrintStream(bout)); + } + + @AfterAll + public static void tearDown() throws IOException { + // Clean up products created for testing + for (Product product : productsToTest) { + Utils.deleteProduct(product.getName()); + } + + System.setOut(origPrintStream); + } + + @Test + public void testSearchWithOffset() throws Exception { + int offset = 1; + SearchOffset.searchWithOffset(PROJECT_ID, VISITOR_ID, PRODUCT_TITLE, offset); + String output = bout.toString(); + assertThat(output) + .contains("Found " + (NUM_PRODUCTS_TO_TEST - offset) + " results in current page"); + assertThat(output).contains("Product Name:"); + } + + @Test + public void testSearchWithOffset_offsetEqualsZero() throws Exception { + int offset = 0; + SearchOffset.searchWithOffset(PROJECT_ID, VISITOR_ID, PRODUCT_TITLE, offset); + String output = bout.toString(); + assertThat(output).contains("Found " + NUM_PRODUCTS_TO_TEST + " results in current page"); + assertThat(output).contains("Product Name:"); + } + + @Test + public void testSearchWithOffset_offsetTooLarge_foundZeroResults() throws Exception { + int offset = 5; + SearchOffset.searchWithOffset(PROJECT_ID, VISITOR_ID, PRODUCT_TITLE, offset); + String output = bout.toString(); + assertThat(output).contains("Found 0 results in current page"); + } + + @Test + public void testSearchWithOffset_negativeOffset_throwsInvalidArgumentException() + throws Exception { + int offset = -1; + assertThrows( + InvalidArgumentException.class, + () -> SearchOffset.searchWithOffset(PROJECT_ID, VISITOR_ID, PRODUCT_TITLE, offset)); + } +} diff --git a/retail/snippets/src/test/java/com/example/search/SearchPaginationIT.java b/retail/snippets/src/test/java/com/example/search/SearchPaginationIT.java new file mode 100644 index 00000000000..5af535ee448 --- /dev/null +++ b/retail/snippets/src/test/java/com/example/search/SearchPaginationIT.java @@ -0,0 +1,101 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.search; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.google.api.gax.rpc.InvalidArgumentException; +import com.google.cloud.retail.v2.Product; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.List; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class SearchPaginationIT { + private static PrintStream origPrintStream; + private static ByteArrayOutputStream bout; + + private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); + private static final String VISITOR_ID = "test_visitor"; + private static final int NUM_PRODUCTS_TO_TEST = 2; + private static final String PRODUCT_TITLE = "Hot Java Testing"; + private static List productsToTest; + + private static void requireEnvVar(String varName) { + assertNotNull( + "Environment variable " + varName + " is required to perform these tests.", + System.getenv(varName)); + } + + @BeforeAll + public static void setUp() throws InterruptedException, IOException { + requireEnvVar("GOOGLE_CLOUD_PROJECT"); + + // Create products to be searched + productsToTest = Utils.createProductsToTest(NUM_PRODUCTS_TO_TEST, PROJECT_ID, PRODUCT_TITLE); + Utils.waitForProductsToBeIndexed(PROJECT_ID, productsToTest, PRODUCT_TITLE); + + origPrintStream = System.out; + bout = new ByteArrayOutputStream(); + System.setOut(new PrintStream(bout)); + } + + @AfterAll + public static void tearDown() throws IOException { + // Clean up products created for testing + for (Product product : productsToTest) { + Utils.deleteProduct(product.getName()); + } + + System.setOut(origPrintStream); + } + + @Test + public void testSearchWithPaginationAndMultiplePages() throws Exception { + SearchPagination.searchWithPagination(PROJECT_ID, VISITOR_ID, PRODUCT_TITLE, 1); + String output = bout.toString(); + assertThat(output).contains("Results of page number 1:"); + assertThat(output).contains("Found 1 results in current page"); + assertThat(output).contains("Results of page number 2:"); + assertThat(output).contains("Found 1 results in current page"); + assertThat(output).contains("No more available pages."); + } + + @Test + public void testSearchWithPaginationAndSinglePage() throws Exception { + SearchPagination.searchWithPagination(PROJECT_ID, VISITOR_ID, PRODUCT_TITLE, 10); + String output = bout.toString(); + assertThat(output).contains("Results of page number 1:"); + assertThat(output).contains("Found " + NUM_PRODUCTS_TO_TEST + " results in current page"); + assertThat(output).contains("No more available pages."); + } + + @Test + public void testSearchWithPagination_negativePageSize_throwsInvalidArgumentException() + throws Exception { + int pageSize = -1; + assertThrows( + InvalidArgumentException.class, + () -> + SearchPagination.searchWithPagination(PROJECT_ID, VISITOR_ID, PRODUCT_TITLE, pageSize)); + } +} diff --git a/retail/snippets/src/test/java/com/example/search/Utils.java b/retail/snippets/src/test/java/com/example/search/Utils.java new file mode 100644 index 00000000000..24f2fd9f5e6 --- /dev/null +++ b/retail/snippets/src/test/java/com/example/search/Utils.java @@ -0,0 +1,126 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.search; + +import com.google.cloud.retail.v2.BranchName; +import com.google.cloud.retail.v2.CreateProductRequest; +import com.google.cloud.retail.v2.DeleteProductRequest; +import com.google.cloud.retail.v2.PriceInfo; +import com.google.cloud.retail.v2.Product; +import com.google.cloud.retail.v2.Product.Availability; +import com.google.cloud.retail.v2.Product.Type; +import com.google.cloud.retail.v2.ProductServiceClient; +import com.google.cloud.retail.v2.SearchRequest; +import com.google.cloud.retail.v2.SearchServiceClient; +import com.google.cloud.retail.v2.SearchServiceClient.SearchPagedResponse; +import com.google.cloud.retail.v2.ServingConfigName; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class Utils { + + public static List createProductsToTest( + int amount, String projectId, String productTitle) throws IOException { + List productIds = new ArrayList<>(); + for (int i = 0; i < amount; i++) { + productIds.add(UUID.randomUUID().toString()); + } + return createProducts(projectId, productIds, productTitle); + } + + public static List createProducts( + String projectId, List productIds, String productTitle) throws IOException { + BranchName branchName = BranchName.of(projectId, "global", "default_catalog", "default_branch"); + + float price = 8.0f; + float originalPrice = 12.0f; + + PriceInfo priceInfo = + PriceInfo.newBuilder() + .setPrice(price) + .setOriginalPrice(originalPrice) + .setCurrencyCode("USD") + .build(); + + Product generatedProduct = + Product.newBuilder() + .setTitle(productTitle) + .setType(Type.PRIMARY) + .addCategories("Beverages") + .addBrands("Google") + .setPriceInfo(priceInfo) + .setAvailability(Availability.IN_STOCK) + .build(); + List createdProducts = new ArrayList<>(); + try (ProductServiceClient serviceClient = ProductServiceClient.create()) { + for (String productId : productIds) { + CreateProductRequest createProductRequest = + CreateProductRequest.newBuilder() + .setProduct(generatedProduct) + .setProductId(productId) + .setParent(branchName.toString()) + .build(); + + createdProducts.add(serviceClient.createProduct(createProductRequest)); + } + } + return createdProducts; + } + + public static void deleteProduct(String productName) throws IOException { + DeleteProductRequest deleteProductRequest = + DeleteProductRequest.newBuilder().setName(productName).build(); + + try (ProductServiceClient serviceClient = ProductServiceClient.create()) { + serviceClient.deleteProduct(deleteProductRequest); + } + } + + public static boolean areProductsReadyToTest(String projectId, String query, int amountOfProducts) + throws IOException { + try (SearchServiceClient searchServiceClient = SearchServiceClient.create()) { + ServingConfigName servingConfigName = + ServingConfigName.of(projectId, "global", "default_catalog", "default_search"); + BranchName branchName = + BranchName.of(projectId, "global", "default_catalog", "default_branch"); + SearchRequest searchRequest = + SearchRequest.newBuilder() + .setPlacement(servingConfigName.toString()) + .setBranch(branchName.toString()) + .setVisitorId("test_visitor") + .setQuery(query) + .setPageSize(amountOfProducts) + .build(); + SearchPagedResponse response = searchServiceClient.search(searchRequest); + + return response.getPage().getResponse().getResultsCount() >= amountOfProducts; + } + } + + public static void waitForProductsToBeIndexed( + String projectId, List products, String productTitle) + throws InterruptedException, IOException { + for (int i = 0; i < 3; i++) { + Thread.sleep(10000); + if (areProductsReadyToTest(projectId, productTitle, products.size())) { + break; + } + } + } +}