From 2d1a01bb2cef458276419720cb9a44d5625dd862 Mon Sep 17 00:00:00 2001 From: tetiana-karasova <62887365+tetiana-karasova@users.noreply.github.com> Date: Fri, 18 Feb 2022 02:17:03 +0100 Subject: [PATCH 1/3] test: fix for product import tests (#156) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: product_number variable is replaced with product_id Import product code samples are added * use project_number in search_simple_query.py * use project_number in search_with_boost_spec.py * use project_number in search_with_filtering.py * use project number in search_with_ordering.py * use project_number in search_with_pagination.py * use project_number in search_with_query_expansion_spec.py * use project_number in search_with_facet_spec.py * use correct path to resources * revert change to paths * resolve error where bq table doesn't exist * use google.cloud.bigquery client * fix for failed tests * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * move samples/interactive-tutorials/product/setup to move samples/interactive-tutorials/product/setup_product * allow import_products_bq_test to run concurrently * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * add dependency for test * remove tests for setup script Co-authored-by: Anthonios Partheniou Co-authored-by: Owl Bot --- samples/interactive-tutorials/README.md | 2 +- ...ducts_delete_gcs_bucket.py => conftest.py} | 18 +-- .../import_products_big_query_table.py | 142 ++++++++++-------- .../product/import_products_bq_test.py | 30 +++- .../product/import_products_gcs.py | 3 +- .../product/import_products_gcs_test.py | 12 +- .../product/import_products_inline_source.py | 4 +- .../product/import_products_inline_test.py | 3 +- .../product/requirements-test.txt | 1 + .../products_create_bigquery_table_test.py | 45 ------ .../setup/products_create_gcs_bucket_test.py | 40 ----- .../products_create_bigquery_table.py | 0 .../products_create_gcs_bucket.py | 0 .../{setup => setup_product}/setup_cleanup.py | 27 ++-- 14 files changed, 150 insertions(+), 177 deletions(-) rename samples/interactive-tutorials/product/{setup/products_delete_gcs_bucket.py => conftest.py} (65%) delete mode 100644 samples/interactive-tutorials/product/setup/products_create_bigquery_table_test.py delete mode 100644 samples/interactive-tutorials/product/setup/products_create_gcs_bucket_test.py rename samples/interactive-tutorials/product/{setup => setup_product}/products_create_bigquery_table.py (100%) rename samples/interactive-tutorials/product/{setup => setup_product}/products_create_gcs_bucket.py (100%) rename samples/interactive-tutorials/product/{setup => setup_product}/setup_cleanup.py (89%) diff --git a/samples/interactive-tutorials/README.md b/samples/interactive-tutorials/README.md index ca7204f7..70fb2f3c 100644 --- a/samples/interactive-tutorials/README.md +++ b/samples/interactive-tutorials/README.md @@ -124,7 +124,7 @@ The bucket name must be unique. For convenience, you can name it ` str: + return prefixer.create_prefix() diff --git a/samples/interactive-tutorials/product/import_products_big_query_table.py b/samples/interactive-tutorials/product/import_products_big_query_table.py index 39b0d6f6..d66fce9e 100644 --- a/samples/interactive-tutorials/product/import_products_big_query_table.py +++ b/samples/interactive-tutorials/product/import_products_big_query_table.py @@ -12,90 +12,104 @@ # 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 -# +import argparse import os -import time - -from google.cloud.retail import ( - BigQuerySource, - ImportProductsRequest, - ProductInputConfig, - ProductServiceClient, -) -project_number = os.environ["GOOGLE_CLOUD_PROJECT_NUMBER"] project_id = os.environ["GOOGLE_CLOUD_PROJECT"] -default_catalog = f"projects/{project_number}/locations/global/catalogs/default_catalog/branches/default_branch" -dataset_id = "products" -table_id = "products" +def main(project_id, dataset_id, table_id): + # [START retail_import_products_from_big_query] + # TODO: Set project_id to your Google Cloud Platform project ID. + # project_id = "my-project" -# TO CHECK ERROR HANDLING USE THE TABLE WITH INVALID PRODUCTS: -# table_id = "products_some_invalid" + # TODO: Set dataset_id + # dataset_id = "products" + # TODO: Set dataset_id + # table_id = "products" -# get import products from big query request -def get_import_products_big_query_request(reconciliation_mode): - # TO CHECK ERROR HANDLING PASTE THE INVALID CATALOG NAME HERE: - # default_catalog = "invalid_catalog_name" - big_query_source = BigQuerySource() - big_query_source.project_id = project_id - big_query_source.dataset_id = dataset_id - big_query_source.table_id = table_id - big_query_source.data_schema = "product" + # Import products into a catalog from big query table using Retail API + import time - input_config = ProductInputConfig() - input_config.big_query_source = big_query_source + from google.cloud.retail import ( + BigQuerySource, + ImportProductsRequest, + ProductInputConfig, + ProductServiceClient, + ) - import_request = ImportProductsRequest() - import_request.parent = default_catalog - import_request.reconciliation_mode = reconciliation_mode - import_request.input_config = input_config + default_catalog = f"projects/{project_id}/locations/global/catalogs/default_catalog/branches/default_branch" - print("---import products from big query table request---") - print(import_request) + # TO CHECK ERROR HANDLING USE THE TABLE WITH INVALID PRODUCTS: + # table_id = "products_some_invalid" - return import_request + # get import products from big query request + def get_import_products_big_query_request(reconciliation_mode): + # TO CHECK ERROR HANDLING PASTE THE INVALID CATALOG NAME HERE: + # default_catalog = "invalid_catalog_name" + big_query_source = BigQuerySource() + big_query_source.project_id = project_id + big_query_source.dataset_id = dataset_id + big_query_source.table_id = table_id + big_query_source.data_schema = "product" + input_config = ProductInputConfig() + input_config.big_query_source = big_query_source -# call the Retail API to import products -def import_products_from_big_query(): - # TRY THE FULL RECONCILIATION MODE HERE: - reconciliation_mode = ImportProductsRequest.ReconciliationMode.INCREMENTAL + import_request = ImportProductsRequest() + import_request.parent = default_catalog + import_request.reconciliation_mode = reconciliation_mode + import_request.input_config = input_config - import_big_query_request = get_import_products_big_query_request( - reconciliation_mode - ) - big_query_operation = ProductServiceClient().import_products( - import_big_query_request - ) + print("---import products from big query table request---") + print(import_request) - print("---the operation was started:----") - print(big_query_operation.operation.name) + return import_request - while not big_query_operation.done(): - print("---please wait till operation is done---") - time.sleep(30) - print("---import products operation is done---") + # call the Retail API to import products + def import_products_from_big_query(): + # TRY THE FULL RECONCILIATION MODE HERE: + reconciliation_mode = ImportProductsRequest.ReconciliationMode.INCREMENTAL - if big_query_operation.metadata is not None: - print("---number of successfully imported products---") - print(big_query_operation.metadata.success_count) - print("---number of failures during the importing---") - print(big_query_operation.metadata.failure_count) - else: - print("---operation.metadata is empty---") + import_big_query_request = get_import_products_big_query_request( + reconciliation_mode + ) + big_query_operation = ProductServiceClient().import_products( + import_big_query_request + ) - if big_query_operation.result is not None: - print("---operation result:---") - print(big_query_operation.result()) - else: - print("---operation.result is empty---") + print("---the operation was started:----") + print(big_query_operation.operation.name) + while not big_query_operation.done(): + print("---please wait till operation is done---") + time.sleep(30) + print("---import products operation is done---") + + if big_query_operation.metadata is not None: + print("---number of successfully imported products---") + print(big_query_operation.metadata.success_count) + print("---number of failures during the importing---") + print(big_query_operation.metadata.failure_count) + else: + print("---operation.metadata is empty---") + + if big_query_operation.result is not None: + print("---operation result:---") + print(big_query_operation.result()) + else: + print("---operation.result is empty---") + + import_products_from_big_query() -import_products_from_big_query() # [END retail_import_products_from_big_query] + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("dataset_id") + parser.add_argument("table_id") + args = parser.parse_args() + main(project_id, args.dataset_id, args.table_id) diff --git a/samples/interactive-tutorials/product/import_products_bq_test.py b/samples/interactive-tutorials/product/import_products_bq_test.py index b743ae72..027ca53c 100644 --- a/samples/interactive-tutorials/product/import_products_bq_test.py +++ b/samples/interactive-tutorials/product/import_products_bq_test.py @@ -15,12 +15,35 @@ import re import subprocess +from setup_product.setup_cleanup import ( + create_bq_dataset, + create_bq_table, + delete_bq_table, + upload_data_to_bq_table, +) + + +def test_import_products_bq(table_id_prefix): + dataset = "products" + valid_products_table = f"{table_id_prefix}products" + product_schema = "../resources/product_schema.json" + valid_products_source_file = "../resources/products.json" + + create_bq_dataset(dataset) + create_bq_table(dataset, valid_products_table, product_schema) + upload_data_to_bq_table( + dataset, valid_products_table, valid_products_source_file, product_schema + ) -def test_import_products_bq(): output = str( - subprocess.check_output("python import_products_big_query_table.py", shell=True) + subprocess.check_output( + f"python import_products_big_query_table.py {dataset} {valid_products_table}", + shell=True, + ) ) + delete_bq_table(dataset, valid_products_table) + assert re.match(".*import products from big query table request.*", output) assert re.match(".*the operation was started.*", output) assert re.match( @@ -28,4 +51,5 @@ def test_import_products_bq(): output, ) - assert re.match(".*number of successfully imported products.*316.*", output) + assert re.match(".*number of successfully imported products.*?316.*", output) + assert re.match(".*number of failures during the importing.*?0.*", output) diff --git a/samples/interactive-tutorials/product/import_products_gcs.py b/samples/interactive-tutorials/product/import_products_gcs.py index 1a865604..1ca5f3e8 100644 --- a/samples/interactive-tutorials/product/import_products_gcs.py +++ b/samples/interactive-tutorials/product/import_products_gcs.py @@ -28,12 +28,11 @@ # Read the project number from the environment variable -project_number = os.environ["GOOGLE_CLOUD_PROJECT_NUMBER"] project_id = os.environ["GOOGLE_CLOUD_PROJECT"] bucket_name = os.environ["BUCKET_NAME"] # You can change the branch here. The "default_branch" is set to point to the branch "0" -default_catalog = f"projects/{project_number}/locations/global/catalogs/default_catalog/branches/default_branch" +default_catalog = f"projects/{project_id}/locations/global/catalogs/default_catalog/branches/default_branch" gcs_bucket = f"gs://{bucket_name}" gcs_errors_bucket = f"{gcs_bucket}/error" diff --git a/samples/interactive-tutorials/product/import_products_gcs_test.py b/samples/interactive-tutorials/product/import_products_gcs_test.py index f8ec4149..706112fd 100644 --- a/samples/interactive-tutorials/product/import_products_gcs_test.py +++ b/samples/interactive-tutorials/product/import_products_gcs_test.py @@ -12,13 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os import re import subprocess +from setup_product.setup_cleanup import create_bucket, delete_bucket, upload_blob + def test_import_products_gcs(): + bucket_name = os.environ["BUCKET_NAME"] + create_bucket(bucket_name) + upload_blob(bucket_name, "../resources/products.json") + output = str(subprocess.check_output("python import_products_gcs.py", shell=True)) + delete_bucket(bucket_name) + assert re.match(".*import products from google cloud source request.*", output) assert re.match('.*input_uris: "gs://.*/products.json".*', output) assert re.match(".*the operation was started.*", output) @@ -27,4 +36,5 @@ def test_import_products_gcs(): output, ) - assert re.match(".*number of successfully imported products.*316.*", output) + assert re.match(".*number of successfully imported products.*?316.*", output) + assert re.match(".*number of failures during the importing.*?0.*", output) diff --git a/samples/interactive-tutorials/product/import_products_inline_source.py b/samples/interactive-tutorials/product/import_products_inline_source.py index 23e89930..5d6434d9 100644 --- a/samples/interactive-tutorials/product/import_products_inline_source.py +++ b/samples/interactive-tutorials/product/import_products_inline_source.py @@ -33,9 +33,9 @@ from google.protobuf.field_mask_pb2 import FieldMask # Read the project number from the environment variable -project_number = os.environ["GOOGLE_CLOUD_PROJECT_NUMBER"] +project_id = os.environ["GOOGLE_CLOUD_PROJECT"] -default_catalog = f"projects/{project_number}/locations/global/catalogs/default_catalog/branches/default_branch" +default_catalog = f"projects/{project_id}/locations/global/catalogs/default_catalog/branches/default_branch" # prepare product to import as inline source diff --git a/samples/interactive-tutorials/product/import_products_inline_test.py b/samples/interactive-tutorials/product/import_products_inline_test.py index 388cd5b6..e8f26d17 100644 --- a/samples/interactive-tutorials/product/import_products_inline_test.py +++ b/samples/interactive-tutorials/product/import_products_inline_test.py @@ -28,4 +28,5 @@ def test_import_products_gcs(): output, ) - assert re.match(".*number of successfully imported products.*2.*", output) + assert re.match(".*number of successfully imported products.*?2.*", output) + assert re.match(".*number of failures during the importing.*?0.*", output) diff --git a/samples/interactive-tutorials/product/requirements-test.txt b/samples/interactive-tutorials/product/requirements-test.txt index bbf73145..6113315e 100644 --- a/samples/interactive-tutorials/product/requirements-test.txt +++ b/samples/interactive-tutorials/product/requirements-test.txt @@ -1,2 +1,3 @@ pytest==6.2.5 pytest-xdist==2.5.0 +google-cloud-testutils==1.3.1 diff --git a/samples/interactive-tutorials/product/setup/products_create_bigquery_table_test.py b/samples/interactive-tutorials/product/setup/products_create_bigquery_table_test.py deleted file mode 100644 index e6eee353..00000000 --- a/samples/interactive-tutorials/product/setup/products_create_bigquery_table_test.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2021 Google Inc. All Rights Reserved. -# -# 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. - -import os -import re -import subprocess - -project_id = os.environ["GOOGLE_CLOUD_PROJECT"] - - -def test_create_bigquery_table(): - output = str( - subprocess.check_output( - 'python setup/products_create_bigquery_table.py', - shell=True)) - assert re.match( - f'.*Creating dataset {project_id}.products.*', output) - assert re.match( - f'(.*dataset {project_id}.products already exists.*|.*dataset is created.*)', output) - assert re.match( - f'.*Creating BigQuery table {project_id}.products.products.*', output) - assert re.match( - f'(.*table {project_id}.products.products already exists.*|.*table is created.*)', output) - assert re.match( - f'.*Uploading data from ../resources/products.json to the table {project_id}.products.products.*', output) - assert re.match( - f'.*Creating BigQuery table {project_id}.products.products_some_invalid.*', - output) - assert re.match( - f'(.*table {project_id}.products.products_some_invalid already exists.*|.*table is created.*)', - output) - assert re.match( - f'.*Uploading data from ../resources/products_some_invalid.json to the table {project_id}.products.products_some_invalid.*', - output) diff --git a/samples/interactive-tutorials/product/setup/products_create_gcs_bucket_test.py b/samples/interactive-tutorials/product/setup/products_create_gcs_bucket_test.py deleted file mode 100644 index 20509745..00000000 --- a/samples/interactive-tutorials/product/setup/products_create_gcs_bucket_test.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2021 Google Inc. All Rights Reserved. -# -# 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. - -import re -import subprocess - -from products_delete_gcs_bucket import delete_bucket_by_name - - -def test_create_gcs_bucket(): - output = str( - subprocess.check_output( - 'python setup/products_create_gcs_bucket.py', - shell=True)) - - bucket_name = re.search('The gcs bucket (.+?) was created', output).group(1) - delete_bucket_by_name(bucket_name) - - print("bucket_name = {}".format(bucket_name)) - - assert re.match( - '.*Creating new bucket.*', output) - assert re.match( - '(.*The gcs bucket.*?was created.*|.*Bucket.*?already exists.*)', output) - assert re.match( - '.*Uploading data from ../resources/products.json to the bucket.*', output) - assert re.match( - '.*Uploading data from ../resources/products_some_invalid.json to the bucket.*', - output) diff --git a/samples/interactive-tutorials/product/setup/products_create_bigquery_table.py b/samples/interactive-tutorials/product/setup_product/products_create_bigquery_table.py similarity index 100% rename from samples/interactive-tutorials/product/setup/products_create_bigquery_table.py rename to samples/interactive-tutorials/product/setup_product/products_create_bigquery_table.py diff --git a/samples/interactive-tutorials/product/setup/products_create_gcs_bucket.py b/samples/interactive-tutorials/product/setup_product/products_create_gcs_bucket.py similarity index 100% rename from samples/interactive-tutorials/product/setup/products_create_gcs_bucket.py rename to samples/interactive-tutorials/product/setup_product/products_create_gcs_bucket.py diff --git a/samples/interactive-tutorials/product/setup/setup_cleanup.py b/samples/interactive-tutorials/product/setup_product/setup_cleanup.py similarity index 89% rename from samples/interactive-tutorials/product/setup/setup_cleanup.py rename to samples/interactive-tutorials/product/setup_product/setup_cleanup.py index 11027b9c..5c04f5e3 100644 --- a/samples/interactive-tutorials/product/setup/setup_cleanup.py +++ b/samples/interactive-tutorials/product/setup_product/setup_cleanup.py @@ -23,10 +23,9 @@ from google.cloud.retail_v2 import CreateProductRequest, DeleteProductRequest, \ FulfillmentInfo, GetProductRequest, PriceInfo, Product, ProductServiceClient -project_number = os.environ["GOOGLE_CLOUD_PROJECT_NUMBER"] project_id = os.environ["GOOGLE_CLOUD_PROJECT"] -default_catalog = f"projects/{project_number}/locations/global/catalogs/default_catalog" -default_branch_name = f"projects/{project_number}/locations/global/catalogs/default_catalog/branches/default_branch" +default_catalog = f"projects/{project_id}/locations/global/catalogs/default_catalog" +default_branch_name = f"projects/{project_id}/locations/global/catalogs/default_catalog/branches/default_branch" def generate_product() -> Product: @@ -100,7 +99,7 @@ def try_to_delete_product_if_exists(product_name: str): def create_bucket(bucket_name: str): """Create a new bucket in Cloud Storage""" print("Creating new bucket:" + bucket_name) - buckets_in_your_project = str(list_buckets()) + buckets_in_your_project = list_buckets() if bucket_name in buckets_in_your_project: print("Bucket {} already exists".format(bucket_name)) else: @@ -118,9 +117,9 @@ def create_bucket(bucket_name: str): def delete_bucket(bucket_name: str): """Delete a bucket from Cloud Storage""" + print(f"Deleting bucket name: {bucket_name}") storage_client = storage.Client() - print("Deleting bucket name:" + bucket_name) - buckets_in_your_project = str(list_buckets()) + buckets_in_your_project = list_buckets() if bucket_name in buckets_in_your_project: blobs = storage_client.list_blobs(bucket_name) for blob in blobs: @@ -138,7 +137,7 @@ def list_buckets(): storage_client = storage.Client() buckets = storage_client.list_buckets() for bucket in buckets: - bucket_list.append(str(bucket)) + bucket_list.append(bucket.name) return bucket_list @@ -194,6 +193,13 @@ def create_bq_table(dataset, table_name, schema_file_path): print("table is created") +def delete_bq_table(dataset, table_name): + full_table_id = f"{project_id}.{dataset}.{table_name}" + bq = bigquery.Client() + bq.delete_table(full_table_id, not_found_ok=True) + print("Table '{}' is deleted.".format(full_table_id)) + + def upload_data_to_bq_table(dataset, table_name, source, schema_file_path): """Upload data to the table from specified source file""" full_table_id = f"{project_id}.{dataset}.{table_name}" @@ -201,8 +207,11 @@ def upload_data_to_bq_table(dataset, table_name, source, schema_file_path): print(f"Uploading data from {source} to the table {full_table_id}") with open(schema_file_path, "rb") as schema: schema_dict = json.load(schema) - job_config = bigquery.LoadJobConfig(source_format=bigquery.SourceFormat.NEWLINE_DELIMITED_JSON, schema=schema_dict) + job_config = bigquery.LoadJobConfig( + source_format=bigquery.SourceFormat.NEWLINE_DELIMITED_JSON, + schema=schema_dict) with open(source, "rb") as source_file: - job = bq.load_table_from_file(source_file, full_table_id, job_config=job_config) + job = bq.load_table_from_file(source_file, full_table_id, + job_config=job_config) job.result() # Waits for the job to complete. print("data was uploaded") From 92d3d525205e27d2fd9d2c3a03f40ef21e1f9ba3 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Fri, 18 Feb 2022 02:44:21 +0100 Subject: [PATCH 2/3] chore(deps): update all dependencies (#153) Co-authored-by: Anthonios Partheniou --- samples/interactive-tutorials/product/requirements-test.txt | 2 +- samples/interactive-tutorials/product/requirements.txt | 6 +++--- samples/interactive-tutorials/search/requirements.txt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/samples/interactive-tutorials/product/requirements-test.txt b/samples/interactive-tutorials/product/requirements-test.txt index 6113315e..3ab16c90 100644 --- a/samples/interactive-tutorials/product/requirements-test.txt +++ b/samples/interactive-tutorials/product/requirements-test.txt @@ -1,3 +1,3 @@ -pytest==6.2.5 +pytest==7.0.1 pytest-xdist==2.5.0 google-cloud-testutils==1.3.1 diff --git a/samples/interactive-tutorials/product/requirements.txt b/samples/interactive-tutorials/product/requirements.txt index 0ba6ea71..259782b1 100644 --- a/samples/interactive-tutorials/product/requirements.txt +++ b/samples/interactive-tutorials/product/requirements.txt @@ -1,4 +1,4 @@ google==3.0.0 -google-cloud-retail==1.1.0 -google-cloud-storage==1.43.0 -google-cloud-bigquery==2.30.1 \ No newline at end of file +google-cloud-retail==1.3.0 +google-cloud-storage==2.1.0 +google-cloud-bigquery==2.33.0 \ No newline at end of file diff --git a/samples/interactive-tutorials/search/requirements.txt b/samples/interactive-tutorials/search/requirements.txt index a54aa8fe..259782b1 100644 --- a/samples/interactive-tutorials/search/requirements.txt +++ b/samples/interactive-tutorials/search/requirements.txt @@ -1,4 +1,4 @@ google==3.0.0 google-cloud-retail==1.3.0 google-cloud-storage==2.1.0 -google-cloud-bigquery==2.32.0 \ No newline at end of file +google-cloud-bigquery==2.33.0 \ No newline at end of file From d8f8e34885146b0a8386c73a5b820cd5216a4ec7 Mon Sep 17 00:00:00 2001 From: tetiana-karasova <62887365+tetiana-karasova@users.noreply.github.com> Date: Fri, 18 Feb 2022 16:56:23 +0100 Subject: [PATCH 3/3] docs(samples): add samples to create, read, update, and delete products (#150) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Retail Tutorials Product CRUD * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix requirements.txt * fix lint errors * lint fix * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix tests * lint * lint * revert change * remove client options * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * remove create/delete bucket which does not work with concurrent tests * lint * allow import_products_gcs_test to run concurrently * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * remove pytest.mark.flaky * lint * fix set inventory test * update copyright year Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .../product/add_fulfillment_places.py | 80 +++++++++ .../product/add_fulfillment_places_test.py | 34 ++++ .../interactive-tutorials/product/conftest.py | 5 + .../product/create_product.py | 81 ++++++++++ .../product/create_product_test.py | 29 ++++ .../product/crud_product.py | 143 ++++++++++++++++ .../product/crud_product_test.py | 36 +++++ .../product/delete_product.py | 60 +++++++ .../product/delete_product_test.py | 31 ++++ .../product/get_product.py | 60 +++++++ .../product/get_product_test.py | 29 ++++ .../import_products_big_query_table.py | 5 +- .../product/import_products_bq_test.py | 2 +- .../product/import_products_gcs.py | 152 +++++++++--------- .../product/import_products_gcs_test.py | 22 ++- .../product/import_products_inline_source.py | 2 +- .../product/import_products_inline_test.py | 2 +- .../product/noxfile_config.py | 1 - .../product/remove_fulfillment_places.py | 80 +++++++++ .../product/remove_fulfillment_places_test.py | 30 ++++ .../product/set_inventory.py | 101 ++++++++++++ .../product/set_inventory_test.py | 39 +++++ .../products_create_bigquery_table.py | 2 +- .../products_create_gcs_bucket.py | 2 +- .../product/setup_product/setup_cleanup.py | 4 +- .../product/update_product.py | 99 ++++++++++++ .../product/update_product_test.py | 27 ++++ .../search/search_simple_query.py | 2 +- .../search/search_simple_query_test.py | 2 +- .../search/search_with_boost_spec.py | 2 +- .../search/search_with_boost_spec_test.py | 2 +- .../search/search_with_facet_spec.py | 2 +- .../search/search_with_facet_spec_test.py | 2 +- .../search/search_with_filtering.py | 2 +- .../search/search_with_filtering_test.py | 3 +- .../search/search_with_ordering.py | 2 +- .../search/search_with_ordering_test.py | 2 +- .../search/search_with_pagination.py | 2 +- .../search/search_with_pagination_test.py | 2 +- .../search_with_query_expansion_spec.py | 2 +- .../search_with_query_expansion_spec_test.py | 2 +- .../create_test_resources.py | 9 +- .../remove_test_resources.py | 11 +- 43 files changed, 1091 insertions(+), 116 deletions(-) create mode 100644 samples/interactive-tutorials/product/add_fulfillment_places.py create mode 100644 samples/interactive-tutorials/product/add_fulfillment_places_test.py create mode 100644 samples/interactive-tutorials/product/create_product.py create mode 100644 samples/interactive-tutorials/product/create_product_test.py create mode 100644 samples/interactive-tutorials/product/crud_product.py create mode 100644 samples/interactive-tutorials/product/crud_product_test.py create mode 100644 samples/interactive-tutorials/product/delete_product.py create mode 100644 samples/interactive-tutorials/product/delete_product_test.py create mode 100644 samples/interactive-tutorials/product/get_product.py create mode 100644 samples/interactive-tutorials/product/get_product_test.py create mode 100644 samples/interactive-tutorials/product/remove_fulfillment_places.py create mode 100644 samples/interactive-tutorials/product/remove_fulfillment_places_test.py create mode 100644 samples/interactive-tutorials/product/set_inventory.py create mode 100644 samples/interactive-tutorials/product/set_inventory_test.py create mode 100644 samples/interactive-tutorials/product/update_product.py create mode 100644 samples/interactive-tutorials/product/update_product_test.py diff --git a/samples/interactive-tutorials/product/add_fulfillment_places.py b/samples/interactive-tutorials/product/add_fulfillment_places.py new file mode 100644 index 00000000..f0340458 --- /dev/null +++ b/samples/interactive-tutorials/product/add_fulfillment_places.py @@ -0,0 +1,80 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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_add_fulfillment_places] +import datetime +import os +import random +import string +import time + +from google.cloud.retail import AddFulfillmentPlacesRequest, ProductServiceClient + +from setup_product.setup_cleanup import create_product, delete_product, get_product + +project_id = os.getenv("GOOGLE_CLOUD_PROJECT") +product_id = "".join(random.sample(string.ascii_lowercase, 8)) +product_name = ( + "projects/" + + project_id + + "/locations/global/catalogs/default_catalog/branches/default_branch/products/" + + product_id +) + +# The request timestamp +current_date = datetime.datetime.now() +outdated_date = datetime.datetime.now() - datetime.timedelta(days=1) + + +# add fulfillment request +def get_add_fulfillment_request( + product_name: str, timestamp, place_id +) -> AddFulfillmentPlacesRequest: + add_fulfillment_request = AddFulfillmentPlacesRequest() + add_fulfillment_request.product = product_name + add_fulfillment_request.type_ = "pickup-in-store" + add_fulfillment_request.place_ids = [place_id] + add_fulfillment_request.add_time = timestamp + add_fulfillment_request.allow_missing = True + + print("---add fulfillment request---") + print(add_fulfillment_request) + + return add_fulfillment_request + + +# add fulfillment places to product +def add_fulfillment_places(product_name: str, timestamp, place_id): + add_fulfillment_request = get_add_fulfillment_request( + product_name, timestamp, place_id + ) + ProductServiceClient().add_fulfillment_places(add_fulfillment_request) + + # This is a long running operation and its result is not immediately present with get operations, + # thus we simulate wait with sleep method. + print("---add fulfillment places, wait 40 seconds :---") + time.sleep(40) + + +# [END retail_add_fulfillment_places] + + +create_product(product_id) +print("------add fulfilment places with current date: {}-----".format(current_date)) +add_fulfillment_places(product_name, current_date, "store2") +get_product(product_name) +print("------add outdated fulfilment places: {}-----".format(outdated_date)) +add_fulfillment_places(product_name, outdated_date, "store3") +get_product(product_name) +delete_product(product_name) diff --git a/samples/interactive-tutorials/product/add_fulfillment_places_test.py b/samples/interactive-tutorials/product/add_fulfillment_places_test.py new file mode 100644 index 00000000..81c6af4d --- /dev/null +++ b/samples/interactive-tutorials/product/add_fulfillment_places_test.py @@ -0,0 +1,34 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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. + +import re +import subprocess + + +def test_add_fulfillment(): + output = str( + subprocess.check_output("python add_fulfillment_places.py", shell=True) + ) + + assert re.match(".*product is created.*", output) + assert re.match(".*add fulfillment request.*", output) + assert re.match(".*add fulfillment places.*", output) + assert re.match( + '.*get product response.*?fulfillment_info.*type_: "pickup-in-store".*?place_ids: "store1".*', + output, + ) + assert re.match( + '.*get product response.*?fulfillment_info.*type_: "pickup-in-store".*?place_ids: "store2".*', + output, + ) diff --git a/samples/interactive-tutorials/product/conftest.py b/samples/interactive-tutorials/product/conftest.py index 31942e59..8cfb8596 100644 --- a/samples/interactive-tutorials/product/conftest.py +++ b/samples/interactive-tutorials/product/conftest.py @@ -23,3 +23,8 @@ @pytest.fixture(scope="session") def table_id_prefix() -> str: return prefixer.create_prefix() + + +@pytest.fixture(scope="session") +def bucket_name_prefix() -> str: + return prefixer.create_prefix() diff --git a/samples/interactive-tutorials/product/create_product.py b/samples/interactive-tutorials/product/create_product.py new file mode 100644 index 00000000..083d3e85 --- /dev/null +++ b/samples/interactive-tutorials/product/create_product.py @@ -0,0 +1,81 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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_create_product] +# Create product in a catalog using Retail API +# +import os +import random +import string + +from google.cloud.retail import CreateProductRequest, Product, ProductServiceClient +from google.cloud.retail import PriceInfo +from google.cloud.retail_v2.types import product + +from setup_product.setup_cleanup import delete_product + +project_id = os.getenv("GOOGLE_CLOUD_PROJECT") +default_branch_name = ( + "projects/" + + project_id + + "/locations/global/catalogs/default_catalog/branches/default_branch" +) +generated_product_id = "".join(random.sample(string.ascii_lowercase, 8)) + + +# generate product to create +def generate_product() -> Product: + price_info = PriceInfo() + price_info.price = 30.0 + price_info.original_price = 35.5 + price_info.currency_code = "USD" + return product.Product( + title="Nest Mini", + type_=product.Product.Type.PRIMARY, + categories=["Speakers and displays"], + brands=["Google"], + price_info=price_info, + availability="IN_STOCK", + ) + + +# get create product request +def get_create_product_request(product_to_create: Product, product_id: str) -> object: + create_product_request = CreateProductRequest() + create_product_request.product = product_to_create + create_product_request.product_id = product_id + create_product_request.parent = default_branch_name + + print("---create product request---") + print(create_product_request) + + return create_product_request + + +# call the Retail API to create product +def create_product(product_id: str): + create_product_request = get_create_product_request(generate_product(), product_id) + product_created = ProductServiceClient().create_product(create_product_request) + + print("---created product:---") + print(product_created) + return product_created + + +# create a product +created_product = create_product(generated_product_id) +# delete created product +delete_product(created_product.name) +# [END retail_create_product] diff --git a/samples/interactive-tutorials/product/create_product_test.py b/samples/interactive-tutorials/product/create_product_test.py new file mode 100644 index 00000000..de2a263e --- /dev/null +++ b/samples/interactive-tutorials/product/create_product_test.py @@ -0,0 +1,29 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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. + +import re +import subprocess + + +def test_create_product(): + output = str(subprocess.check_output("python create_product.py", shell=True)) + + assert re.match(".*create product request.*", output) + assert re.match(".*created product.*", output) + assert re.match( + '.*name: "projects/.+/locations/global/catalogs/default_catalog/branches/0/products/.*', + output, + ) + assert re.match('.*title: "Nest Mini".*', output) + assert re.match(".*product.*was deleted.*", output) diff --git a/samples/interactive-tutorials/product/crud_product.py b/samples/interactive-tutorials/product/crud_product.py new file mode 100644 index 00000000..4ae922f0 --- /dev/null +++ b/samples/interactive-tutorials/product/crud_product.py @@ -0,0 +1,143 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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_crud_product] +# Create product in a catalog using Retail API +# +import os +import random +import string + +from google.cloud.retail import ( + CreateProductRequest, + DeleteProductRequest, + GetProductRequest, + Product, + ProductServiceClient, + UpdateProductRequest, +) +from google.cloud.retail import PriceInfo +from google.cloud.retail_v2.types import product + +project_id = os.getenv("GOOGLE_CLOUD_PROJECT") +default_branch_name = ( + "projects/" + + project_id + + "/locations/global/catalogs/default_catalog/branches/default_branch" +) +product_id = "".join(random.sample(string.ascii_lowercase, 8)) +product_name = "{}/products/{}".format(default_branch_name, product_id) + + +# generate product for create +def generate_product() -> Product: + price_info = PriceInfo() + price_info.price = 30.0 + price_info.original_price = 35.5 + price_info.currency_code = "USD" + return product.Product( + title="Nest Mini", + type_=product.Product.Type.PRIMARY, + categories=["Speakers and displays"], + brands=["Google"], + price_info=price_info, + availability="IN_STOCK", + ) + + +# generate product for update +def generate_product_for_update() -> Product: + price_info = PriceInfo() + price_info.price = 20.0 + price_info.original_price = 25.5 + price_info.currency_code = "EUR" + return product.Product( + id=product_id, + name=product_name, + title="Updated Nest Mini", + type_=product.Product.Type.PRIMARY, + categories=["Updated Speakers and displays"], + brands=["Updated Google"], + availability="OUT_OF_STOCK", + price_info=price_info, + ) + + +# create product +def create_product() -> object: + create_product_request = CreateProductRequest() + create_product_request.product = generate_product() + create_product_request.product_id = product_id + create_product_request.parent = default_branch_name + + print("---create product request---") + print(create_product_request) + + product_created = ProductServiceClient().create_product(create_product_request) + print("---created product:---") + print(product_created) + return product_created + + +# get product +def get_product() -> object: + get_product_request = GetProductRequest() + get_product_request.name = product_name + + print("---get product request---") + print(get_product_request) + + get_product_response = ProductServiceClient().get_product(get_product_request) + + print("---get product response:---") + print(get_product_response) + return get_product_response + + +# update product +def update_product(): + update_product_request = UpdateProductRequest() + update_product_request.product = generate_product_for_update() + update_product_request.allow_missing = True + + print("---update product request---") + print(update_product_request) + + updated_product = ProductServiceClient().update_product(update_product_request) + print("---updated product---:") + print(updated_product) + return updated_product + + +# delete product +def delete_product(): + delete_product_request = DeleteProductRequest() + delete_product_request.name = product_name + + print("---delete product request---") + print(delete_product_request) + + ProductServiceClient().delete_product(delete_product_request) + + print("deleting product " + product_name) + print("---product was deleted:---") + + +# call the methods +create_product() +get_product() +update_product() +delete_product() +# [END retail_crud_product] diff --git a/samples/interactive-tutorials/product/crud_product_test.py b/samples/interactive-tutorials/product/crud_product_test.py new file mode 100644 index 00000000..fbea8034 --- /dev/null +++ b/samples/interactive-tutorials/product/crud_product_test.py @@ -0,0 +1,36 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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. + +import re +import subprocess + + +def test_add_fulfillment(): + output = str(subprocess.check_output("python crud_product.py", shell=True)) + + assert re.match(".*create product request.*", output) + assert re.match(".*create product request.*", output) + assert re.match('.*create product request.*?title: "Nest Mini".*', output) + assert re.match(".*created product.*", output) + assert re.match('.*created product.*?title: "Nest Mini".*', output) + + assert re.match(".*get product request.*", output) + assert re.match(".*get product response.*", output) + + assert re.match(".*update product request.*", output) + assert re.match('.*update product request.*?title: "Updated Nest Mini.*', output) + assert re.match(".*updated product.*?title.*?Updated Nest Mini.*", output) + assert re.match(".*updated product.*?brands.*?Updated Google.*", output) + assert re.match(".*updated product.*?price.*?20.*", output) + assert re.match(".*product was deleted.*", output) diff --git a/samples/interactive-tutorials/product/delete_product.py b/samples/interactive-tutorials/product/delete_product.py new file mode 100644 index 00000000..9cda4ff7 --- /dev/null +++ b/samples/interactive-tutorials/product/delete_product.py @@ -0,0 +1,60 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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_delete_product] +# Delete product from a catalog using Retail API +# +import os +import random +import string + +from google.cloud.retail import DeleteProductRequest, ProductServiceClient + +from setup_product.setup_cleanup import create_product + +project_id = os.getenv("GOOGLE_CLOUD_PROJECT") +default_branch_name = ( + "projects/" + + project_id + + "/locations/global/catalogs/default_catalog/branches/default_branch" +) +product_id = "".join(random.sample(string.ascii_lowercase, 8)) + + +# get delete product request +def get_delete_product_request(product_name: str): + delete_product_request = DeleteProductRequest() + delete_product_request.name = product_name + + print("---delete product request---") + print(delete_product_request) + + return delete_product_request + + +# call the Retail API to delete product +def delete_product(product_name: str): + delete_product_request = get_delete_product_request(product_name) + ProductServiceClient().delete_product(delete_product_request) + + print("deleting product " + product_name) + print("---product was deleted:---") + + +# delete created product +created_product_name = create_product(product_id).name +delete_product(created_product_name) + +# [END retail_delete_product] diff --git a/samples/interactive-tutorials/product/delete_product_test.py b/samples/interactive-tutorials/product/delete_product_test.py new file mode 100644 index 00000000..7bd36074 --- /dev/null +++ b/samples/interactive-tutorials/product/delete_product_test.py @@ -0,0 +1,31 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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. + +import re +import subprocess + + +def test_delete_product(): + output = str(subprocess.check_output("python delete_product.py", shell=True)) + + assert re.match(".*delete product request.*", output) + assert re.match( + '.*name: "projects/.+/locations/global/catalogs/default_catalog/branches/0/products/.*', + output, + ) + assert re.match( + ".*deleting product projects/.+/locations/global/catalogs/default_catalog/branches/0/products/.*", + output, + ) + assert re.match(".*product was deleted.*", output) diff --git a/samples/interactive-tutorials/product/get_product.py b/samples/interactive-tutorials/product/get_product.py new file mode 100644 index 00000000..c864bdde --- /dev/null +++ b/samples/interactive-tutorials/product/get_product.py @@ -0,0 +1,60 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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_get_product] +# Get product from a catalog using Retail API +# +import os +import random +import string + +from google.cloud.retail import GetProductRequest, ProductServiceClient + +from setup_product.setup_cleanup import create_product, delete_product + +project_id = os.getenv("GOOGLE_CLOUD_PROJECT") +product_id = "".join(random.sample(string.ascii_lowercase, 8)) + + +# get product request +def get_product_request(product_name: str) -> object: + get_product_request = GetProductRequest() + get_product_request.name = product_name + + print("---get product request---") + print(get_product_request) + + return get_product_request + + +# call the Retail API to get product +def get_product(product_name: str): + # get a product from catalog + get_request = get_product_request(product_name) + get_product_response = ProductServiceClient().get_product(get_request) + + print("---get product response:---") + print(get_product_response) + return get_product_response + + +# create a product +created_product = create_product(product_id) +# get created product +product = get_product(created_product.name) +# remove created product +delete_product(created_product.name) + +# [END retail_get_product] diff --git a/samples/interactive-tutorials/product/get_product_test.py b/samples/interactive-tutorials/product/get_product_test.py new file mode 100644 index 00000000..3f50af0a --- /dev/null +++ b/samples/interactive-tutorials/product/get_product_test.py @@ -0,0 +1,29 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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. + +import re +import subprocess + + +def test_get_product(): + output = str(subprocess.check_output("python get_product.py", shell=True)) + + assert re.match(".*get product request.*", output) + assert re.match(".*get product response.*", output) + assert re.match( + ".*get product response.*?name.*?projects/.*/locations/global/catalogs/default_catalog/branches/0/products/.*", + output, + ) + assert re.match(".*get product response.*?title.*?Nest Mini.*", output) + assert re.match(".*product.*was deleted.*", output) diff --git a/samples/interactive-tutorials/product/import_products_big_query_table.py b/samples/interactive-tutorials/product/import_products_big_query_table.py index d66fce9e..91976cc9 100644 --- a/samples/interactive-tutorials/product/import_products_big_query_table.py +++ b/samples/interactive-tutorials/product/import_products_big_query_table.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -103,8 +103,7 @@ def import_products_from_big_query(): import_products_from_big_query() - -# [END retail_import_products_from_big_query] + # [END retail_import_products_from_big_query] if __name__ == "__main__": diff --git a/samples/interactive-tutorials/product/import_products_bq_test.py b/samples/interactive-tutorials/product/import_products_bq_test.py index 027ca53c..a388365b 100644 --- a/samples/interactive-tutorials/product/import_products_bq_test.py +++ b/samples/interactive-tutorials/product/import_products_bq_test.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/product/import_products_gcs.py b/samples/interactive-tutorials/product/import_products_gcs.py index 1ca5f3e8..abcb6230 100644 --- a/samples/interactive-tutorials/product/import_products_gcs.py +++ b/samples/interactive-tutorials/product/import_products_gcs.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,99 +12,105 @@ # 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 -# -import os -import time +import argparse -from google.cloud.retail import ( - GcsSource, - ImportErrorsConfig, - ImportProductsRequest, - ProductInputConfig, - ProductServiceClient, -) +def main(bucket_name): + # [START retail_import_products_from_gcs] -# Read the project number from the environment variable -project_id = os.environ["GOOGLE_CLOUD_PROJECT"] -bucket_name = os.environ["BUCKET_NAME"] + import os + import time -# You can change the branch here. The "default_branch" is set to point to the branch "0" -default_catalog = f"projects/{project_id}/locations/global/catalogs/default_catalog/branches/default_branch" + from google.cloud.retail import ( + GcsSource, + ImportErrorsConfig, + ImportProductsRequest, + ProductInputConfig, + ProductServiceClient, + ) -gcs_bucket = f"gs://{bucket_name}" -gcs_errors_bucket = f"{gcs_bucket}/error" -gcs_products_object = "products.json" + # Read the project id from the environment variable + project_id = os.environ["GOOGLE_CLOUD_PROJECT"] + # TODO: Developer set the bucket_name + # bucket_name = os.environ["BUCKET_NAME"] -# TO CHECK ERROR HANDLING USE THE JSON WITH INVALID PRODUCT -# gcs_products_object = "products_some_invalid.json" + # You can change the branch here. The "default_branch" is set to point to the branch "0" + default_catalog = f"projects/{project_id}/locations/global/catalogs/default_catalog/branches/default_branch" + gcs_bucket = f"gs://{bucket_name}" + gcs_errors_bucket = f"{gcs_bucket}/error" + gcs_products_object = "products.json" -# get import products from gcs request -def get_import_products_gcs_request(gcs_object_name: str): - # TO CHECK ERROR HANDLING PASTE THE INVALID CATALOG NAME HERE: - # default_catalog = "invalid_catalog_name" - gcs_source = GcsSource() - gcs_source.input_uris = [f"{gcs_bucket}/{gcs_object_name}"] + # TO CHECK ERROR HANDLING USE THE JSON WITH INVALID PRODUCT + # gcs_products_object = "products_some_invalid.json" - input_config = ProductInputConfig() - input_config.gcs_source = gcs_source - print("GRS source:") - print(gcs_source.input_uris) + def get_import_products_gcs_request(gcs_object_name: str): + # TO CHECK ERROR HANDLING PASTE THE INVALID CATALOG NAME HERE: + # default_catalog = "invalid_catalog_name" + gcs_source = GcsSource() + gcs_source.input_uris = [f"{gcs_bucket}/{gcs_object_name}"] - errors_config = ImportErrorsConfig() - errors_config.gcs_prefix = gcs_errors_bucket + input_config = ProductInputConfig() + input_config.gcs_source = gcs_source + print("GRS source:") + print(gcs_source.input_uris) - import_request = ImportProductsRequest() - import_request.parent = default_catalog - import_request.reconciliation_mode = ( - ImportProductsRequest.ReconciliationMode.INCREMENTAL - ) - import_request.input_config = input_config - import_request.errors_config = errors_config + errors_config = ImportErrorsConfig() + errors_config.gcs_prefix = gcs_errors_bucket - print("---import products from google cloud source request---") - print(import_request) + import_request = ImportProductsRequest() + import_request.parent = default_catalog + import_request.reconciliation_mode = ( + ImportProductsRequest.ReconciliationMode.INCREMENTAL + ) + import_request.input_config = input_config + import_request.errors_config = errors_config - return import_request + print("---import products from google cloud source request---") + print(import_request) + return import_request -# call the Retail API to import products -def import_products_from_gcs(): - import_gcs_request = get_import_products_gcs_request(gcs_products_object) - gcs_operation = ProductServiceClient().import_products(import_gcs_request) + # call the Retail API to import products + def import_products_from_gcs(): + import_gcs_request = get_import_products_gcs_request(gcs_products_object) + gcs_operation = ProductServiceClient().import_products(import_gcs_request) - print("---the operation was started:----") - print(gcs_operation.operation.name) + print("---the operation was started:----") + print(gcs_operation.operation.name) - while not gcs_operation.done(): - print("---please wait till operation is done---") - time.sleep(30) - print("---import products operation is done---") + while not gcs_operation.done(): + print("---please wait till operation is done---") + time.sleep(30) + print("---import products operation is done---") - if gcs_operation.metadata is not None: - print("---number of successfully imported products---") - print(gcs_operation.metadata.success_count) - print("---number of failures during the importing---") - print(gcs_operation.metadata.failure_count) - else: - print("---operation.metadata is empty---") + if gcs_operation.metadata is not None: + print("---number of successfully imported products---") + print(gcs_operation.metadata.success_count) + print("---number of failures during the importing---") + print(gcs_operation.metadata.failure_count) + else: + print("---operation.metadata is empty---") - if gcs_operation.result is not None: - print("---operation result:---") - print(gcs_operation.result()) - else: - print("---operation.result is empty---") + if gcs_operation.result is not None: + print("---operation result:---") + print(gcs_operation.result()) + else: + print("---operation.result is empty---") - # The imported products needs to be indexed in the catalog before they become available for search. - print( - "Wait 2-5 minutes till products become indexed in the catalog, after that they will be available for search" - ) + # The imported products needs to be indexed in the catalog before they become available for search. + print( + "Wait 2-5 minutes till products become indexed in the catalog, after that they will be available for search" + ) + + import_products_from_gcs() + # [END retail_import_products_from_gcs] -import_products_from_gcs() -# [END retail_import_products_from_gcs] +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("bucket_name") + args = parser.parse_args() + main(args.bucket_name) diff --git a/samples/interactive-tutorials/product/import_products_gcs_test.py b/samples/interactive-tutorials/product/import_products_gcs_test.py index 706112fd..2b79b2fd 100644 --- a/samples/interactive-tutorials/product/import_products_gcs_test.py +++ b/samples/interactive-tutorials/product/import_products_gcs_test.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,21 +12,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os import re import subprocess from setup_product.setup_cleanup import create_bucket, delete_bucket, upload_blob -def test_import_products_gcs(): - bucket_name = os.environ["BUCKET_NAME"] - create_bucket(bucket_name) - upload_blob(bucket_name, "../resources/products.json") +def test_import_products_gcs(bucket_name_prefix): + # gcs buckets have a limit of 63 characters. Get the last 60 characters + bucket_name = bucket_name_prefix[63:] - output = str(subprocess.check_output("python import_products_gcs.py", shell=True)) + try: + create_bucket(bucket_name) + upload_blob(bucket_name, "../resources/products.json") - delete_bucket(bucket_name) + output = str( + subprocess.check_output( + f"python import_products_gcs.py {bucket_name}", shell=True + ) + ) + finally: + delete_bucket(bucket_name) assert re.match(".*import products from google cloud source request.*", output) assert re.match('.*input_uris: "gs://.*/products.json".*', output) diff --git a/samples/interactive-tutorials/product/import_products_inline_source.py b/samples/interactive-tutorials/product/import_products_inline_source.py index 5d6434d9..f0fbeef0 100644 --- a/samples/interactive-tutorials/product/import_products_inline_source.py +++ b/samples/interactive-tutorials/product/import_products_inline_source.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/product/import_products_inline_test.py b/samples/interactive-tutorials/product/import_products_inline_test.py index e8f26d17..a504d99b 100644 --- a/samples/interactive-tutorials/product/import_products_inline_test.py +++ b/samples/interactive-tutorials/product/import_products_inline_test.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/product/noxfile_config.py b/samples/interactive-tutorials/product/noxfile_config.py index eeab78b4..8008bc71 100644 --- a/samples/interactive-tutorials/product/noxfile_config.py +++ b/samples/interactive-tutorials/product/noxfile_config.py @@ -19,7 +19,6 @@ TEST_CONFIG_OVERRIDE = { # You can opt out from the test for specific Python versions. - "ignored_versions": ["2.7", "3.6"], # An envvar key for determining the project id to use. Change it # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a # build specific Cloud project. You can also use your own string diff --git a/samples/interactive-tutorials/product/remove_fulfillment_places.py b/samples/interactive-tutorials/product/remove_fulfillment_places.py new file mode 100644 index 00000000..387e7ec0 --- /dev/null +++ b/samples/interactive-tutorials/product/remove_fulfillment_places.py @@ -0,0 +1,80 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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_remove_fulfillment_places] +import datetime +import os +import random +import string +import time + +from google.cloud.retail import ProductServiceClient, RemoveFulfillmentPlacesRequest + +from setup_product.setup_cleanup import create_product, delete_product, get_product + +project_id = os.getenv("GOOGLE_CLOUD_PROJECT") +product_id = "".join(random.sample(string.ascii_lowercase, 8)) +product_name = ( + "projects/" + + project_id + + "/locations/global/catalogs/default_catalog/branches/default_branch/products/" + + product_id +) + +# The request timestamp +current_date = datetime.datetime.now() +outdated_date = datetime.datetime.now() - datetime.timedelta(days=1) + + +# remove fulfillment request +def get_remove_fulfillment_request( + product_name: str, timestamp, store_id +) -> RemoveFulfillmentPlacesRequest: + remove_fulfillment_request = RemoveFulfillmentPlacesRequest() + remove_fulfillment_request.product = product_name + remove_fulfillment_request.type_ = "pickup-in-store" + remove_fulfillment_request.place_ids = [store_id] + remove_fulfillment_request.remove_time = timestamp + remove_fulfillment_request.allow_missing = True + + print("---remove fulfillment request---") + print(remove_fulfillment_request) + + return remove_fulfillment_request + + +# remove fulfillment places to product +def remove_fulfillment_places(product_name: str, timestamp, store_id): + remove_fulfillment_request = get_remove_fulfillment_request( + product_name, timestamp, store_id + ) + ProductServiceClient().remove_fulfillment_places(remove_fulfillment_request) + + # This is a long running operation and its result is not immediately present with get operations, + # thus we simulate wait with sleep method. + print("---remove fulfillment places, wait 40 seconds:---") + time.sleep(40) + + +# [END retail_remove_fulfillment_places] + + +create_product(product_id) +print("------remove fulfilment places with current date: {}-----".format(current_date)) +remove_fulfillment_places(product_name, current_date, "store0") +get_product(product_name) +print("------remove outdated fulfilment places: {}-----".format(outdated_date)) +remove_fulfillment_places(product_name, outdated_date, "store1") +get_product(product_name) +delete_product(product_name) diff --git a/samples/interactive-tutorials/product/remove_fulfillment_places_test.py b/samples/interactive-tutorials/product/remove_fulfillment_places_test.py new file mode 100644 index 00000000..e747e42e --- /dev/null +++ b/samples/interactive-tutorials/product/remove_fulfillment_places_test.py @@ -0,0 +1,30 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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. + +import re +import subprocess + + +def test_add_fulfillment(): + output = str( + subprocess.check_output("python remove_fulfillment_places.py", shell=True) + ) + + assert re.match(".*product is created.*", output) + assert re.match(".*remove fulfillment request.*", output) + assert re.match(".*remove fulfillment places.*", output) + assert re.match( + '.*get product response.*?fulfillment_info.*type_: "pickup-in-store".*?place_ids: "store1".*', + output, + ) diff --git a/samples/interactive-tutorials/product/set_inventory.py b/samples/interactive-tutorials/product/set_inventory.py new file mode 100644 index 00000000..d0908875 --- /dev/null +++ b/samples/interactive-tutorials/product/set_inventory.py @@ -0,0 +1,101 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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_set_inventory] +import datetime +import os +import random +import string +import time + +from google.cloud.retail import ( + FulfillmentInfo, + PriceInfo, + ProductServiceClient, + SetInventoryRequest, +) +from google.cloud.retail import Product +from google.protobuf.field_mask_pb2 import FieldMask + +from setup_product.setup_cleanup import create_product, delete_product, get_product + +project_id = os.getenv("GOOGLE_CLOUD_PROJECT") +product_id = "".join(random.sample(string.ascii_lowercase, 8)) +product_name = ( + "projects/" + + project_id + + "/locations/global/catalogs/default_catalog/branches/default_branch/products/" + + product_id +) + + +# product inventory info +def get_product_with_inventory_info(product_name: str) -> Product: + price_info = PriceInfo() + price_info.price = 15.0 + price_info.original_price = 20.0 + price_info.cost = 8.0 + price_info.currency_code = "USD" + + fulfillment_info = FulfillmentInfo() + fulfillment_info.type_ = "pickup-in-store" + fulfillment_info.place_ids = ["store1", "store2"] + + product = Product() + product.name = product_name + product.price_info = price_info + product.fulfillment_info = [fulfillment_info] + product.availability = "IN_STOCK" + + return product + + +# set inventory request +def get_set_inventory_request(product_name: str) -> SetInventoryRequest: + # The request timestamp + request_time = datetime.datetime.now() + # The out-of-order request timestamp + # request_time = datetime.datetime.now() - datetime.timedelta(days=1) + set_mask = FieldMask( + paths=["price_info", "availability", "fulfillment_info", "available_quantity"] + ) + + set_inventory_request = SetInventoryRequest() + set_inventory_request.inventory = get_product_with_inventory_info(product_name) + set_inventory_request.set_time = request_time + set_inventory_request.allow_missing = True + set_inventory_request.set_mask = set_mask + + print("---set inventory request---") + print(set_inventory_request) + + return set_inventory_request + + +# set inventory to product +def set_inventory(product_name: str): + set_inventory_request = get_set_inventory_request(product_name) + ProductServiceClient().set_inventory(set_inventory_request) + + # This is a long running operation and its result is not immediately present with get operations, + # thus we simulate wait with sleep method. + print("---set inventory, wait 60 seconds:---") + time.sleep(60) + + +create_product(product_id) +set_inventory(product_name) +get_product(product_name) +delete_product(product_name) +# [END retail_set_inventory] diff --git a/samples/interactive-tutorials/product/set_inventory_test.py b/samples/interactive-tutorials/product/set_inventory_test.py new file mode 100644 index 00000000..0b5b8ab4 --- /dev/null +++ b/samples/interactive-tutorials/product/set_inventory_test.py @@ -0,0 +1,39 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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. + +import re +import subprocess + + +def test_set_inventory(): + output = str(subprocess.check_output("python set_inventory.py", shell=True)) + print(output) + assert re.match(".*product is created.*", output) + assert re.match( + '.*name: "projects/.*/locations/global/catalogs/default_catalog/branches/0/products.*', + output, + ) + assert re.match(".*set inventory request.*", output) + assert re.match( + '.*get product response.*?fulfillment_info.*type_: "pickup-in-store".*?place_ids: "store1".*', + output, + ) + assert re.match( + '.*get product response.*?fulfillment_info.*type_: "pickup-in-store".*?place_ids: "store2".*', + output, + ) + assert re.match( + ".*product projects/.*/locations/global/catalogs/default_catalog/branches/default_branch/products.* was deleted.*", + output, + ) diff --git a/samples/interactive-tutorials/product/setup_product/products_create_bigquery_table.py b/samples/interactive-tutorials/product/setup_product/products_create_bigquery_table.py index 1ee25c56..493893bf 100644 --- a/samples/interactive-tutorials/product/setup_product/products_create_bigquery_table.py +++ b/samples/interactive-tutorials/product/setup_product/products_create_bigquery_table.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/product/setup_product/products_create_gcs_bucket.py b/samples/interactive-tutorials/product/setup_product/products_create_gcs_bucket.py index 92be7aa1..64dc6351 100644 --- a/samples/interactive-tutorials/product/setup_product/products_create_gcs_bucket.py +++ b/samples/interactive-tutorials/product/setup_product/products_create_gcs_bucket.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/product/setup_product/setup_cleanup.py b/samples/interactive-tutorials/product/setup_product/setup_cleanup.py index 5c04f5e3..2704326a 100644 --- a/samples/interactive-tutorials/product/setup_product/setup_cleanup.py +++ b/samples/interactive-tutorials/product/setup_product/setup_cleanup.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ from google.cloud import bigquery from google.cloud import storage -from google.cloud.retail_v2 import CreateProductRequest, DeleteProductRequest, \ +from google.cloud.retail import CreateProductRequest, DeleteProductRequest, \ FulfillmentInfo, GetProductRequest, PriceInfo, Product, ProductServiceClient project_id = os.environ["GOOGLE_CLOUD_PROJECT"] diff --git a/samples/interactive-tutorials/product/update_product.py b/samples/interactive-tutorials/product/update_product.py new file mode 100644 index 00000000..6d292cbb --- /dev/null +++ b/samples/interactive-tutorials/product/update_product.py @@ -0,0 +1,99 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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_update_product] +# Update product in a catalog using Retail API +# +import os +import random +import string + +from google.cloud.retail import ( + PriceInfo, + Product, + ProductServiceClient, + UpdateProductRequest, +) +from google.cloud.retail_v2.types import product + +from setup_product.setup_cleanup import create_product, delete_product + +# from google.protobuf.field_mask_pb2 import FieldMask +# from google.protobuf.field_mask_pb2 import FieldMask + + +project_id = os.getenv("GOOGLE_CLOUD_PROJECT") +default_branch_name = ( + "projects/" + + project_id + + "/locations/global/catalogs/default_catalog/branches/default_branch" +) +generated_product_id = "".join(random.sample(string.ascii_lowercase, 8)) + + +# generate product for update +def generate_product_for_update(product_id: str) -> Product: + price_info = PriceInfo() + price_info.price = 20.0 + price_info.original_price = 25.5 + price_info.currency_code = "EUR" + return product.Product( + id=product_id, + name="projects/" + + project_id + + "/locations/global/catalogs/default_catalog/branches/default_branch/products/" + + product_id, + title="Updated Nest Mini", + type_=product.Product.Type.PRIMARY, + categories=["Updated Speakers and displays"], + brands=["Updated Google"], + availability="OUT_OF_STOCK", + price_info=price_info, + ) + + +# get update product request +def get_update_product_request(product_to_update: Product): + update_product_request = UpdateProductRequest() + update_product_request.product = product_to_update + update_product_request.allow_missing = True + # PASTE UPDATE MASK HERE: # the import FieldMask from google.protobuf.field_mask_pb2 is required + + print("---update product request---") + print(update_product_request) + + return update_product_request + + +# call the Retail API to update product +def update_product(original_product: Product): + # update product + updated_product = ProductServiceClient().update_product( + get_update_product_request(generate_product_for_update(original_product.id)) + ) + + print("---updated product---:") + print(updated_product) + return updated_product + + +# create product +created_product = create_product(generated_product_id) +# UPDATE PRODUCT +update_product(created_product) +# delete product +delete_product(created_product.name) + +# [END retail_update_product] diff --git a/samples/interactive-tutorials/product/update_product_test.py b/samples/interactive-tutorials/product/update_product_test.py new file mode 100644 index 00000000..f0b22a0e --- /dev/null +++ b/samples/interactive-tutorials/product/update_product_test.py @@ -0,0 +1,27 @@ +# Copyright 2022 Google Inc. All Rights Reserved. +# +# 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. + +import re +import subprocess + + +def test_add_fulfillment(): + output = str(subprocess.check_output("python update_product.py", shell=True)) + + assert re.match(".*product is created.*", output) + assert re.match(".*updated product.*", output) + assert re.match(".*updated product.*?title.*?Updated Nest Mini.*", output) + assert re.match(".*updated product.*?brands.*?Updated Google.*", output) + assert re.match(".*updated product.*?price.*?20.*", output) + assert re.match(".*product.*was deleted.*", output) diff --git a/samples/interactive-tutorials/search/search_simple_query.py b/samples/interactive-tutorials/search/search_simple_query.py index 78889209..f145dd1d 100644 --- a/samples/interactive-tutorials/search/search_simple_query.py +++ b/samples/interactive-tutorials/search/search_simple_query.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/search/search_simple_query_test.py b/samples/interactive-tutorials/search/search_simple_query_test.py index fc6d0c69..f7f6277f 100644 --- a/samples/interactive-tutorials/search/search_simple_query_test.py +++ b/samples/interactive-tutorials/search/search_simple_query_test.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/search/search_with_boost_spec.py b/samples/interactive-tutorials/search/search_with_boost_spec.py index 7b2995d8..10495b3e 100644 --- a/samples/interactive-tutorials/search/search_with_boost_spec.py +++ b/samples/interactive-tutorials/search/search_with_boost_spec.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/search/search_with_boost_spec_test.py b/samples/interactive-tutorials/search/search_with_boost_spec_test.py index cf391b2e..ce263f18 100644 --- a/samples/interactive-tutorials/search/search_with_boost_spec_test.py +++ b/samples/interactive-tutorials/search/search_with_boost_spec_test.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/search/search_with_facet_spec.py b/samples/interactive-tutorials/search/search_with_facet_spec.py index 50a8fb7f..95863180 100644 --- a/samples/interactive-tutorials/search/search_with_facet_spec.py +++ b/samples/interactive-tutorials/search/search_with_facet_spec.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/search/search_with_facet_spec_test.py b/samples/interactive-tutorials/search/search_with_facet_spec_test.py index df082ac2..0c30561e 100644 --- a/samples/interactive-tutorials/search/search_with_facet_spec_test.py +++ b/samples/interactive-tutorials/search/search_with_facet_spec_test.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/search/search_with_filtering.py b/samples/interactive-tutorials/search/search_with_filtering.py index e7a8a31d..fd14f871 100644 --- a/samples/interactive-tutorials/search/search_with_filtering.py +++ b/samples/interactive-tutorials/search/search_with_filtering.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/search/search_with_filtering_test.py b/samples/interactive-tutorials/search/search_with_filtering_test.py index 5b10ae34..5a3a46f9 100644 --- a/samples/interactive-tutorials/search/search_with_filtering_test.py +++ b/samples/interactive-tutorials/search/search_with_filtering_test.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -37,4 +37,3 @@ def test_search_with_filtering(): assert re.match(".*Tee.*", product_title) assert re.match(".*Black.*", product_title) assert "Black" in response.results[0].product.color_info.color_families - assert response.total_size == 16 diff --git a/samples/interactive-tutorials/search/search_with_ordering.py b/samples/interactive-tutorials/search/search_with_ordering.py index 118d4202..85a7ee0e 100644 --- a/samples/interactive-tutorials/search/search_with_ordering.py +++ b/samples/interactive-tutorials/search/search_with_ordering.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/search/search_with_ordering_test.py b/samples/interactive-tutorials/search/search_with_ordering_test.py index ec8fcfb9..3783d483 100644 --- a/samples/interactive-tutorials/search/search_with_ordering_test.py +++ b/samples/interactive-tutorials/search/search_with_ordering_test.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/search/search_with_pagination.py b/samples/interactive-tutorials/search/search_with_pagination.py index 727227c2..a8160475 100644 --- a/samples/interactive-tutorials/search/search_with_pagination.py +++ b/samples/interactive-tutorials/search/search_with_pagination.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/search/search_with_pagination_test.py b/samples/interactive-tutorials/search/search_with_pagination_test.py index 49bcd63a..3255313f 100644 --- a/samples/interactive-tutorials/search/search_with_pagination_test.py +++ b/samples/interactive-tutorials/search/search_with_pagination_test.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/search/search_with_query_expansion_spec.py b/samples/interactive-tutorials/search/search_with_query_expansion_spec.py index 9455e900..f530a7c9 100644 --- a/samples/interactive-tutorials/search/search_with_query_expansion_spec.py +++ b/samples/interactive-tutorials/search/search_with_query_expansion_spec.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/search/search_with_query_expansion_spec_test.py b/samples/interactive-tutorials/search/search_with_query_expansion_spec_test.py index b9c04e3f..99abdd18 100644 --- a/samples/interactive-tutorials/search/search_with_query_expansion_spec_test.py +++ b/samples/interactive-tutorials/search/search_with_query_expansion_spec_test.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/interactive-tutorials/test_resources_recovery/create_test_resources.py b/samples/interactive-tutorials/test_resources_recovery/create_test_resources.py index 36807af3..c8822075 100644 --- a/samples/interactive-tutorials/test_resources_recovery/create_test_resources.py +++ b/samples/interactive-tutorials/test_resources_recovery/create_test_resources.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,12 +18,11 @@ import subprocess import time -from google.cloud.storage.bucket import Bucket - from google.cloud import storage from google.cloud.retail import GcsSource, ImportErrorsConfig, \ ImportProductsRequest, ProductInputConfig -from google.cloud.retail_v2 import ProductServiceClient +from google.cloud.retail import ProductServiceClient +from google.cloud.storage.bucket import Bucket project_number = os.environ["GOOGLE_CLOUD_PROJECT_NUMBER"] products_bucket_name = os.environ['BUCKET_NAME'] @@ -45,6 +44,7 @@ storage_client = storage.Client() + def create_bucket(bucket_name: str) -> Bucket: """Create a new bucket in Cloud Storage""" print("Creating new bucket:" + bucket_name) @@ -134,6 +134,7 @@ def import_products_from_gcs(): "Wait 2 -5 minutes till products become indexed in the catalog,\ after that they will be available for search") + def create_bq_dataset(dataset_name): """Create a BigQuery dataset""" print("Creating dataset {}".format(dataset_name)) diff --git a/samples/interactive-tutorials/test_resources_recovery/remove_test_resources.py b/samples/interactive-tutorials/test_resources_recovery/remove_test_resources.py index 0877b4bc..a82cc67d 100644 --- a/samples/interactive-tutorials/test_resources_recovery/remove_test_resources.py +++ b/samples/interactive-tutorials/test_resources_recovery/remove_test_resources.py @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. All Rights Reserved. +# Copyright 2022 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,12 +16,12 @@ import shlex import subprocess -from google.api_core.exceptions import PermissionDenied -from google.cloud.storage.bucket import Bucket - +from google.api_core.exceptions import NotFound, PermissionDenied from google.cloud import storage from google.cloud.retail import DeleteProductRequest, ListProductsRequest, \ ProductServiceClient +from google.cloud.storage.bucket import Bucket + project_number = os.environ["GOOGLE_CLOUD_PROJECT_NUMBER"] project_id = os.environ["GOOGLE_CLOUD_PROJECT"] @@ -40,7 +40,7 @@ def delete_bucket(bucket_name): """Delete bucket""" try: bucket = storage_client.get_bucket(bucket_name) - except: + except NotFound: print("Bucket {} does not exists".format(bucket_name)) else: delete_object_from_bucket(bucket) @@ -82,6 +82,7 @@ def delete_bq_dataset_with_tables(dataset): output = subprocess.check_output(shlex.split(delete_dataset_command)) print(output) + delete_bucket(product_bucket_name) delete_bucket(events_bucket_name) delete_all_products()