diff --git a/retail/interactive-tutorials/src/main/java/product/ImportProductsBigQueryTable.java b/retail/interactive-tutorials/src/main/java/product/ImportProductsBigQueryTable.java new file mode 100644 index 00000000000..456ffcea378 --- /dev/null +++ b/retail/interactive-tutorials/src/main/java/product/ImportProductsBigQueryTable.java @@ -0,0 +1,114 @@ +/* + * Copyright 2022 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. + */ + +// [START retail_import_products_from_big_query] + +/* + * Import products into a catalog from big query table using Retail API + */ + +package product; + +import com.google.cloud.retail.v2.BigQuerySource; +import com.google.cloud.retail.v2.ImportMetadata; +import com.google.cloud.retail.v2.ImportProductsRequest; +import com.google.cloud.retail.v2.ImportProductsRequest.ReconciliationMode; +import com.google.cloud.retail.v2.ImportProductsResponse; +import com.google.cloud.retail.v2.ProductInputConfig; +import com.google.cloud.retail.v2.ProductServiceClient; +import com.google.longrunning.Operation; +import com.google.longrunning.OperationsClient; +import java.io.IOException; + +public class ImportProductsBigQueryTable { + + private static final String PROJECT_ID = System.getenv("PROJECT_ID"); + private static final String DEFAULT_CATALOG = + String.format( + "projects/%s/locations/global/catalogs/default_catalog/" + "branches/default_branch", + PROJECT_ID); + private static final String DATASET_ID = "products"; + private static final String TABLE_ID = "products"; + // TO CHECK ERROR HANDLING USE THE TABLE WITH INVALID PRODUCTS: + // TABLE_ID = "products_some_invalid" + private static final String DATA_SCHEMA = "product"; + + public static void main(String[] args) throws IOException, InterruptedException { + // TRY THE FULL RECONCILIATION MODE HERE: + ReconciliationMode reconciliationMode = ReconciliationMode.INCREMENTAL; + ImportProductsRequest importBigQueryRequest = + getImportProductsBigQueryRequest(reconciliationMode); + waitForOperationCompletion(importBigQueryRequest); + } + + public static ImportProductsRequest getImportProductsBigQueryRequest( + ReconciliationMode reconciliationMode) { + BigQuerySource bigQuerySource = + BigQuerySource.newBuilder() + .setProjectId(PROJECT_ID) + .setDatasetId(DATASET_ID) + .setTableId(TABLE_ID) + .setDataSchema(DATA_SCHEMA) + .build(); + + ProductInputConfig inputConfig = + ProductInputConfig.newBuilder().setBigQuerySource(bigQuerySource).build(); + + ImportProductsRequest importRequest = + ImportProductsRequest.newBuilder() + .setParent(DEFAULT_CATALOG) + .setReconciliationMode(reconciliationMode) + .setInputConfig(inputConfig) + .build(); + System.out.printf("Import products from big query table request: %s%n", importRequest); + + return importRequest; + } + + private static void waitForOperationCompletion(ImportProductsRequest importRequest) + throws IOException, InterruptedException { + try (ProductServiceClient serviceClient = ProductServiceClient.create()) { + String operationName = serviceClient.importProductsCallable().call(importRequest).getName(); + System.out.printf("OperationName = %s\n", operationName); + + OperationsClient operationsClient = serviceClient.getOperationsClient(); + Operation operation = operationsClient.getOperation(operationName); + + while (!operation.getDone()) { + // Keep polling the operation periodically until the import task is done. + int awaitDuration = 30000; + Thread.sleep(awaitDuration); + operation = operationsClient.getOperation(operationName); + } + + if (operation.hasMetadata()) { + ImportMetadata metadata = operation.getMetadata().unpack(ImportMetadata.class); + System.out.printf( + "Number of successfully imported products: %s\n", metadata.getSuccessCount()); + System.out.printf( + "Number of failures during the importing: %s\n", metadata.getFailureCount()); + } + + if (operation.hasResponse()) { + ImportProductsResponse response = + operation.getResponse().unpack(ImportProductsResponse.class); + System.out.printf("Operation result: %s%n", response); + } + } + } +} + +// [END retail_import_products_from_big_query] diff --git a/retail/interactive-tutorials/src/main/java/product/ImportProductsGcs.java b/retail/interactive-tutorials/src/main/java/product/ImportProductsGcs.java new file mode 100644 index 00000000000..d56b529b12a --- /dev/null +++ b/retail/interactive-tutorials/src/main/java/product/ImportProductsGcs.java @@ -0,0 +1,117 @@ +/* + * Copyright 2022 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. + */ + +// [START retail_import_products_from_gcs] + +/* + * Import products into a catalog from gcs using Retail API + */ + +package product; + +import com.google.cloud.retail.v2.GcsSource; +import com.google.cloud.retail.v2.ImportErrorsConfig; +import com.google.cloud.retail.v2.ImportMetadata; +import com.google.cloud.retail.v2.ImportProductsRequest; +import com.google.cloud.retail.v2.ImportProductsRequest.ReconciliationMode; +import com.google.cloud.retail.v2.ImportProductsResponse; +import com.google.cloud.retail.v2.ProductInputConfig; +import com.google.cloud.retail.v2.ProductServiceClient; +import com.google.longrunning.Operation; +import com.google.longrunning.OperationsClient; +import java.io.IOException; +import java.util.Collections; + +public class ImportProductsGcs { + + private static final String PROJECT_ID = System.getenv("PROJECT_ID"); + private static final String DEFAULT_CATALOG = + String.format( + "projects/%s/locations/global/catalogs/default_catalog/" + "branches/default_branch", + PROJECT_ID); + private static final String GCS_BUCKET = String.format("gs://%s", System.getenv("BUCKET_NAME")); + private static final String GCS_ERROR_BUCKET = String.format("%s/errors", GCS_BUCKET); + private static final String GCS_PRODUCTS_OBJECT = "products.json"; + // TO CHECK ERROR HANDLING USE THE JSON WITH INVALID PRODUCT + // GCS_PRODUCTS_OBJECT = "products_some_invalid.json" + + public static void main(String[] args) throws IOException, InterruptedException { + ImportProductsRequest importGcsRequest = getImportProductsGcsRequest(GCS_PRODUCTS_OBJECT); + waitForOperationCompletion(importGcsRequest); + } + + public static ImportProductsRequest getImportProductsGcsRequest(String gcsObjectName) { + GcsSource gcsSource = + GcsSource.newBuilder() + .addAllInputUris( + Collections.singleton(String.format("%s/%s", GCS_BUCKET, gcsObjectName))) + .build(); + + ProductInputConfig inputConfig = + ProductInputConfig.newBuilder().setGcsSource(gcsSource).build(); + + System.out.println("GRS source: " + gcsSource.getInputUrisList()); + + ImportErrorsConfig errorsConfig = + ImportErrorsConfig.newBuilder().setGcsPrefix(GCS_ERROR_BUCKET).build(); + + ImportProductsRequest importRequest = + ImportProductsRequest.newBuilder() + .setParent(DEFAULT_CATALOG) + .setReconciliationMode(ReconciliationMode.INCREMENTAL) + .setInputConfig(inputConfig) + .setErrorsConfig(errorsConfig) + .build(); + + System.out.println("Import products from google cloud source request: " + importRequest); + + return importRequest; + } + + private static void waitForOperationCompletion(ImportProductsRequest importRequest) + throws IOException, InterruptedException { + try (ProductServiceClient serviceClient = ProductServiceClient.create()) { + String operationName = serviceClient.importProductsCallable().call(importRequest).getName(); + System.out.printf("OperationName = %s\n", operationName); + + OperationsClient operationsClient = serviceClient.getOperationsClient(); + Operation operation = operationsClient.getOperation(operationName); + + while (!operation.getDone()) { + // Keep polling the operation periodically until the import task is done. + int awaitDuration = 30000; + Thread.sleep(awaitDuration); + operation = operationsClient.getOperation(operationName); + } + + if (operation.hasMetadata()) { + ImportMetadata metadata = operation.getMetadata().unpack(ImportMetadata.class); + System.out.printf( + "Number of successfully imported products: %s\n", metadata.getSuccessCount()); + System.out.printf( + "Number of failures during the importing: %s\n", metadata.getFailureCount()); + } + + if (operation.hasResponse()) { + ImportProductsResponse response = + operation.getResponse().unpack(ImportProductsResponse.class); + System.out.printf("Operation result: %s%n", response); + } + } + } +} + +// [END retail_import_products_from_gcs] diff --git a/retail/interactive-tutorials/src/main/java/product/ImportProductsInlineSource.java b/retail/interactive-tutorials/src/main/java/product/ImportProductsInlineSource.java new file mode 100644 index 00000000000..0c72b14c9f2 --- /dev/null +++ b/retail/interactive-tutorials/src/main/java/product/ImportProductsInlineSource.java @@ -0,0 +1,211 @@ +/* + * Copyright 2022 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. + */ + +// [START retail_import_products_from_inline_source] + +/* + * Import products into a catalog from inline source using Retail API + */ + +package product; + +import com.google.cloud.retail.v2.ColorInfo; +import com.google.cloud.retail.v2.FulfillmentInfo; +import com.google.cloud.retail.v2.ImportMetadata; +import com.google.cloud.retail.v2.ImportProductsRequest; +import com.google.cloud.retail.v2.ImportProductsResponse; +import com.google.cloud.retail.v2.PriceInfo; +import com.google.cloud.retail.v2.Product; +import com.google.cloud.retail.v2.ProductInlineSource; +import com.google.cloud.retail.v2.ProductInputConfig; +import com.google.cloud.retail.v2.ProductServiceClient; +import com.google.longrunning.Operation; +import com.google.longrunning.OperationsClient; +import com.google.protobuf.FieldMask; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +public class ImportProductsInlineSource { + + private static final String PROJECT_ID = System.getenv("PROJECT_ID"); + private static final String DEFAULT_CATALOG = + String.format( + "projects/%s/locations/global/catalogs/default_catalog/" + "branches/default_branch", + PROJECT_ID); + + public static void main(String[] args) throws IOException, InterruptedException { + ImportProductsRequest importRequest = getImportProductsInlineRequest(getProducts()); + waitForOperationCompletion(importRequest); + } + + public static ImportProductsRequest getImportProductsInlineRequest( + List productsToImport) { + ProductInlineSource inlineSource = + ProductInlineSource.newBuilder().addAllProducts(productsToImport).build(); + + ProductInputConfig inputConfig = + ProductInputConfig.newBuilder().setProductInlineSource(inlineSource).build(); + + ImportProductsRequest importRequest = + ImportProductsRequest.newBuilder() + .setParent(DEFAULT_CATALOG) + .setInputConfig(inputConfig) + .build(); + + System.out.printf("Import products from inline source request: %s%n", importRequest); + + return importRequest; + } + + public static List getProducts() { + List products = new ArrayList<>(); + + Product product1; + Product product2; + + float price1 = 16f; + float originalPrice1 = 45.0f; + float cost1 = 12.0f; + + PriceInfo priceInfo1 = + PriceInfo.newBuilder() + .setPrice(price1) + .setOriginalPrice(originalPrice1) + .setCost(cost1) + .setCurrencyCode("USD") + .build(); + + ColorInfo colorInfo1 = + ColorInfo.newBuilder() + .addColorFamilies("Blue") + .addAllColors(Arrays.asList("Light blue", "Blue", "Dark blue")) + .build(); + + FulfillmentInfo fulfillmentInfo1 = + FulfillmentInfo.newBuilder() + .setType("pickup-in-store") + .addAllPlaceIds(Arrays.asList("store1", "store2")) + .build(); + + FieldMask fieldMask1 = + FieldMask.newBuilder() + .addAllPaths(Arrays.asList("title", "categories", "price_info", "color_info")) + .build(); + + // TO CHECK ERROR HANDLING COMMENT OUT THE PRODUCT TITLE HERE: + product1 = + Product.newBuilder() + .setTitle("#IamRemarkable Pen") + .setId(UUID.randomUUID().toString()) + .addAllCategories(Collections.singletonList("Office")) + .setUri( + "https://shop.googlemerchandisestore.com/Google+Redesign/" + + "Office/IamRemarkable+Pen") + .addBrands("#IamRemarkable") + .setPriceInfo(priceInfo1) + .setColorInfo(colorInfo1) + .addFulfillmentInfo(fulfillmentInfo1) + .setRetrievableFields(fieldMask1) + .build(); + + float price2 = 35f; + float originalPrice2 = 45.0f; + float cost2 = 12.0f; + + PriceInfo priceInfo2 = + PriceInfo.newBuilder() + .setPrice(price2) + .setOriginalPrice(originalPrice2) + .setCost(cost2) + .setCurrencyCode("USD") + .build(); + + ColorInfo colorInfo2 = + ColorInfo.newBuilder() + .addColorFamilies("Blue") + .addAllColors(Collections.singletonList("Sky blue")) + .build(); + + FulfillmentInfo fulfillmentInfo2 = + FulfillmentInfo.newBuilder() + .setType("pickup-in-store") + .addAllPlaceIds(Arrays.asList("store2", "store3")) + .build(); + + FieldMask fieldMask2 = + FieldMask.newBuilder() + .addAllPaths(Arrays.asList("title", "categories", "price_info", "color_info")) + .build(); + + product2 = + Product.newBuilder() + .setTitle("Android Embroidered Crewneck Sweater") + .setId(UUID.randomUUID().toString()) + .addCategories("Apparel") + .setUri( + "https://shop.googlemerchandisestore.com/Google+Redesign/" + + "Apparel/Android+Embroidered+Crewneck+Sweater") + .addBrands("Android") + .setPriceInfo(priceInfo2) + .setColorInfo(colorInfo2) + .addFulfillmentInfo(fulfillmentInfo2) + .setRetrievableFields(fieldMask2) + .build(); + + products.add(product1); + products.add(product2); + + return products; + } + + private static void waitForOperationCompletion(ImportProductsRequest importRequest) + throws IOException, InterruptedException { + try (ProductServiceClient serviceClient = ProductServiceClient.create()) { + String operationName = serviceClient.importProductsCallable().call(importRequest).getName(); + System.out.printf("OperationName = %s\n", operationName); + + OperationsClient operationsClient = serviceClient.getOperationsClient(); + Operation operation = operationsClient.getOperation(operationName); + + while (!operation.getDone()) { + // Keep polling the operation periodically until the import task is done. + int awaitDuration = 30000; + Thread.sleep(awaitDuration); + operation = operationsClient.getOperation(operationName); + } + + if (operation.hasMetadata()) { + ImportMetadata metadata = operation.getMetadata().unpack(ImportMetadata.class); + System.out.printf( + "Number of successfully imported products: %s\n", metadata.getSuccessCount()); + System.out.printf( + "Number of failures during the importing: %s\n", metadata.getFailureCount()); + } + + if (operation.hasResponse()) { + ImportProductsResponse response = + operation.getResponse().unpack(ImportProductsResponse.class); + System.out.printf("Operation result: %s%n", response); + } + } + } +} + +// [END retail_import_products_from_inline_source] diff --git a/retail/interactive-tutorials/src/test/java/product/ImportProductsBigQueryTableTest.java b/retail/interactive-tutorials/src/test/java/product/ImportProductsBigQueryTableTest.java new file mode 100644 index 00000000000..c2cc5ca0098 --- /dev/null +++ b/retail/interactive-tutorials/src/test/java/product/ImportProductsBigQueryTableTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2022 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 product; + +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import util.StreamGobbler; + +public class ImportProductsBigQueryTableTest { + + private String output; + + @Before + public void setUp() throws IOException, InterruptedException, ExecutionException { + Process exec = + Runtime.getRuntime() + .exec("mvn compile exec:java -Dexec.mainClass=product.ImportProductsBigQueryTable"); + StreamGobbler streamGobbler = new StreamGobbler(exec.getInputStream()); + Future stringFuture = Executors.newSingleThreadExecutor().submit(streamGobbler); + + output = stringFuture.get(); + } + + @Test + public void testImportProductsBigQueryTable() { + Assert.assertTrue(output.matches("(?s)^(.*Import products from big query table request.*)$")); + Assert.assertTrue( + output.matches( + "(?s)^(.*projects/.*/locations/global/catalogs/default_catalog/branches/0/operations/import-products.*)$")); + Assert.assertTrue(output.matches("(?s)^(.*Operation result.*)$")); + } +} diff --git a/retail/interactive-tutorials/src/test/java/product/ImportProductsGcsTest.java b/retail/interactive-tutorials/src/test/java/product/ImportProductsGcsTest.java new file mode 100644 index 00000000000..eaafa4cd742 --- /dev/null +++ b/retail/interactive-tutorials/src/test/java/product/ImportProductsGcsTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2022 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 product; + +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import util.StreamGobbler; + +public class ImportProductsGcsTest { + + private String output; + + @Before + public void setUp() throws IOException, InterruptedException, ExecutionException { + Process exec = + Runtime.getRuntime() + .exec("mvn compile exec:java -Dexec.mainClass=product.ImportProductsGcs"); + StreamGobbler streamGobbler = new StreamGobbler(exec.getInputStream()); + Future stringFuture = Executors.newSingleThreadExecutor().submit(streamGobbler); + + output = stringFuture.get(); + } + + @Test + public void testImportProductsGcs() { + Assert.assertTrue( + output.matches("(?s)^(.*Import products from google cloud source request.*)$")); + Assert.assertTrue(output.matches("(?s)^(.*input_uris: \"gs://.*/products.json\".*)$")); + Assert.assertTrue( + output.matches( + "(?s)^(.*projects/.*/locations/global/catalogs/default_catalog/branches/0/operations/import-products.*)$")); + Assert.assertTrue(output.matches("(?s)^(.*Number of successfully imported products:.*316.*)$")); + Assert.assertTrue(output.matches("(?s)^(.*Operation result.*)$")); + } +} diff --git a/retail/interactive-tutorials/src/test/java/product/ImportProductsInlineSourceTest.java b/retail/interactive-tutorials/src/test/java/product/ImportProductsInlineSourceTest.java new file mode 100644 index 00000000000..7059c1db067 --- /dev/null +++ b/retail/interactive-tutorials/src/test/java/product/ImportProductsInlineSourceTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2022 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 product; + +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import util.StreamGobbler; + +public class ImportProductsInlineSourceTest { + + private String output; + + @Before + public void setUp() throws IOException, InterruptedException, ExecutionException { + Process exec = + Runtime.getRuntime() + .exec("mvn compile exec:java -Dexec.mainClass=product.ImportProductsInlineSource"); + StreamGobbler streamGobbler = new StreamGobbler(exec.getInputStream()); + Future stringFuture = Executors.newSingleThreadExecutor().submit(streamGobbler); + + output = stringFuture.get(); + } + + @Test + public void testImportProductsInlineSource() { + Assert.assertTrue(output.matches("(?s)^(.*Import products from inline source request.*)$")); + Assert.assertTrue( + output.matches( + "(?s)^(.*projects/.*/locations/global/catalogs/default_catalog/branches/0/operations/import-products.*)$")); + } +}