// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package google.cloud.retail.v2;

import "google/api/annotations.proto";
import "google/api/client.proto";
import "google/api/field_behavior.proto";
import "google/api/resource.proto";
import "google/cloud/retail/v2/common.proto";
import "google/cloud/retail/v2/import_config.proto";
import "google/cloud/retail/v2/product.proto";
import "google/cloud/retail/v2/purge_config.proto";
import "google/longrunning/operations.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/timestamp.proto";

option csharp_namespace = "Google.Cloud.Retail.V2";
option go_package = "cloud.google.com/go/retail/apiv2/retailpb;retailpb";
option java_multiple_files = true;
option java_outer_classname = "ProductServiceProto";
option java_package = "com.google.cloud.retail.v2";
option objc_class_prefix = "RETAIL";
option php_namespace = "Google\\Cloud\\Retail\\V2";
option ruby_package = "Google::Cloud::Retail::V2";

// Service for ingesting [Product][google.cloud.retail.v2.Product] information
// of the customer's website.
service ProductService {
  option (google.api.default_host) = "retail.googleapis.com";
  option (google.api.oauth_scopes) =
      "https://www.googleapis.com/auth/cloud-platform";

  // Creates a [Product][google.cloud.retail.v2.Product].
  rpc CreateProduct(CreateProductRequest) returns (Product) {
    option (google.api.http) = {
      post: "/v2/{parent=projects/*/locations/*/catalogs/*/branches/*}/products"
      body: "product"
    };
    option (google.api.method_signature) = "parent,product,product_id";
  }

  // Gets a [Product][google.cloud.retail.v2.Product].
  rpc GetProduct(GetProductRequest) returns (Product) {
    option (google.api.http) = {
      get: "/v2/{name=projects/*/locations/*/catalogs/*/branches/*/products/**}"
    };
    option (google.api.method_signature) = "name";
  }

  // Gets a list of [Product][google.cloud.retail.v2.Product]s.
  rpc ListProducts(ListProductsRequest) returns (ListProductsResponse) {
    option (google.api.http) = {
      get: "/v2/{parent=projects/*/locations/*/catalogs/*/branches/*}/products"
    };
    option (google.api.method_signature) = "parent";
  }

  // Updates a [Product][google.cloud.retail.v2.Product].
  rpc UpdateProduct(UpdateProductRequest) returns (Product) {
    option (google.api.http) = {
      patch: "/v2/{product.name=projects/*/locations/*/catalogs/*/branches/*/products/**}"
      body: "product"
    };
    option (google.api.method_signature) = "product,update_mask";
  }

  // Deletes a [Product][google.cloud.retail.v2.Product].
  rpc DeleteProduct(DeleteProductRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/v2/{name=projects/*/locations/*/catalogs/*/branches/*/products/**}"
    };
    option (google.api.method_signature) = "name";
  }

  // Permanently deletes all selected [Product][google.cloud.retail.v2.Product]s
  // under a branch.
  //
  // This process is asynchronous. If the request is valid, the removal will be
  // enqueued and processed offline. Depending on the number of
  // [Product][google.cloud.retail.v2.Product]s, this operation could take hours
  // to complete. Before the operation completes, some
  // [Product][google.cloud.retail.v2.Product]s may still be returned by
  // [ProductService.GetProduct][google.cloud.retail.v2.ProductService.GetProduct]
  // or
  // [ProductService.ListProducts][google.cloud.retail.v2.ProductService.ListProducts].
  //
  // Depending on the number of [Product][google.cloud.retail.v2.Product]s, this
  // operation could take hours to complete. To get a sample of
  // [Product][google.cloud.retail.v2.Product]s that would be deleted, set
  // [PurgeProductsRequest.force][google.cloud.retail.v2.PurgeProductsRequest.force]
  // to false.
  rpc PurgeProducts(PurgeProductsRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v2/{parent=projects/*/locations/*/catalogs/*/branches/*}/products:purge"
      body: "*"
    };
    option (google.longrunning.operation_info) = {
      response_type: "google.cloud.retail.v2.PurgeProductsResponse"
      metadata_type: "google.cloud.retail.v2.PurgeProductsMetadata"
    };
  }

  // Bulk import of multiple [Product][google.cloud.retail.v2.Product]s.
  //
  // Request processing may be synchronous.
  // Non-existing items are created.
  //
  // Note that it is possible for a subset of the
  // [Product][google.cloud.retail.v2.Product]s to be successfully updated.
  rpc ImportProducts(ImportProductsRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v2/{parent=projects/*/locations/*/catalogs/*/branches/*}/products:import"
      body: "*"
    };
    option (google.longrunning.operation_info) = {
      response_type: "google.cloud.retail.v2.ImportProductsResponse"
      metadata_type: "google.cloud.retail.v2.ImportMetadata"
    };
  }

  // Updates inventory information for a
  // [Product][google.cloud.retail.v2.Product] while respecting the last update
  // timestamps of each inventory field.
  //
  // This process is asynchronous and does not require the
  // [Product][google.cloud.retail.v2.Product] to exist before updating
  // fulfillment information. If the request is valid, the update is enqueued
  // and processed downstream. As a consequence, when a response is returned,
  // updates are not immediately manifested in the
  // [Product][google.cloud.retail.v2.Product] queried by
  // [ProductService.GetProduct][google.cloud.retail.v2.ProductService.GetProduct]
  // or
  // [ProductService.ListProducts][google.cloud.retail.v2.ProductService.ListProducts].
  //
  // When inventory is updated with
  // [ProductService.CreateProduct][google.cloud.retail.v2.ProductService.CreateProduct]
  // and
  // [ProductService.UpdateProduct][google.cloud.retail.v2.ProductService.UpdateProduct],
  // the specified inventory field value(s) overwrite any existing value(s)
  // while ignoring the last update time for this field. Furthermore, the last
  // update times for the specified inventory fields are overwritten by the
  // times of the
  // [ProductService.CreateProduct][google.cloud.retail.v2.ProductService.CreateProduct]
  // or
  // [ProductService.UpdateProduct][google.cloud.retail.v2.ProductService.UpdateProduct]
  // request.
  //
  // If no inventory fields are set in
  // [CreateProductRequest.product][google.cloud.retail.v2.CreateProductRequest.product],
  // then any pre-existing inventory information for this product is used.
  //
  // If no inventory fields are set in
  // [SetInventoryRequest.set_mask][google.cloud.retail.v2.SetInventoryRequest.set_mask],
  // then any existing inventory information is preserved.
  //
  // Pre-existing inventory information can only be updated with
  // [ProductService.SetInventory][google.cloud.retail.v2.ProductService.SetInventory],
  // [ProductService.AddFulfillmentPlaces][google.cloud.retail.v2.ProductService.AddFulfillmentPlaces],
  // and
  // [ProductService.RemoveFulfillmentPlaces][google.cloud.retail.v2.ProductService.RemoveFulfillmentPlaces].
  //
  // The returned [Operation][google.longrunning.Operation]s is obsolete after
  // one day, and the [GetOperation][google.longrunning.Operations.GetOperation]
  // API returns `NOT_FOUND` afterwards.
  //
  // If conflicting updates are issued, the
  // [Operation][google.longrunning.Operation]s associated with the stale
  // updates are not marked as [done][google.longrunning.Operation.done] until
  // they are obsolete.
  rpc SetInventory(SetInventoryRequest) returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v2/{inventory.name=projects/*/locations/*/catalogs/*/branches/*/products/**}:setInventory"
      body: "*"
    };
    option (google.api.method_signature) = "inventory,set_mask";
    option (google.longrunning.operation_info) = {
      response_type: "google.cloud.retail.v2.SetInventoryResponse"
      metadata_type: "google.cloud.retail.v2.SetInventoryMetadata"
    };
  }

  // We recommend that you use the
  // [ProductService.AddLocalInventories][google.cloud.retail.v2.ProductService.AddLocalInventories]
  // method instead of the
  // [ProductService.AddFulfillmentPlaces][google.cloud.retail.v2.ProductService.AddFulfillmentPlaces]
  // method.
  // [ProductService.AddLocalInventories][google.cloud.retail.v2.ProductService.AddLocalInventories]
  // achieves the same results but provides more fine-grained control over
  // ingesting local inventory data.
  //
  // Incrementally adds place IDs to
  // [Product.fulfillment_info.place_ids][google.cloud.retail.v2.FulfillmentInfo.place_ids].
  //
  // This process is asynchronous and does not require the
  // [Product][google.cloud.retail.v2.Product] to exist before updating
  // fulfillment information. If the request is valid, the update will be
  // enqueued and processed downstream. As a consequence, when a response is
  // returned, the added place IDs are not immediately manifested in the
  // [Product][google.cloud.retail.v2.Product] queried by
  // [ProductService.GetProduct][google.cloud.retail.v2.ProductService.GetProduct]
  // or
  // [ProductService.ListProducts][google.cloud.retail.v2.ProductService.ListProducts].
  //
  // The returned [Operation][google.longrunning.Operation]s will be obsolete
  // after 1 day, and [GetOperation][google.longrunning.Operations.GetOperation]
  // API will return NOT_FOUND afterwards.
  //
  // If conflicting updates are issued, the
  // [Operation][google.longrunning.Operation]s associated with the stale
  // updates will not be marked as [done][google.longrunning.Operation.done]
  // until being obsolete.
  rpc AddFulfillmentPlaces(AddFulfillmentPlacesRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v2/{product=projects/*/locations/*/catalogs/*/branches/*/products/**}:addFulfillmentPlaces"
      body: "*"
    };
    option (google.api.method_signature) = "product";
    option (google.longrunning.operation_info) = {
      response_type: "google.cloud.retail.v2.AddFulfillmentPlacesResponse"
      metadata_type: "google.cloud.retail.v2.AddFulfillmentPlacesMetadata"
    };
  }

  // We recommend that you use the
  // [ProductService.RemoveLocalInventories][google.cloud.retail.v2.ProductService.RemoveLocalInventories]
  // method instead of the
  // [ProductService.RemoveFulfillmentPlaces][google.cloud.retail.v2.ProductService.RemoveFulfillmentPlaces]
  // method.
  // [ProductService.RemoveLocalInventories][google.cloud.retail.v2.ProductService.RemoveLocalInventories]
  // achieves the same results but provides more fine-grained control over
  // ingesting local inventory data.
  //
  // Incrementally removes place IDs from a
  // [Product.fulfillment_info.place_ids][google.cloud.retail.v2.FulfillmentInfo.place_ids].
  //
  // This process is asynchronous and does not require the
  // [Product][google.cloud.retail.v2.Product] to exist before updating
  // fulfillment information. If the request is valid, the update will be
  // enqueued and processed downstream. As a consequence, when a response is
  // returned, the removed place IDs are not immediately manifested in the
  // [Product][google.cloud.retail.v2.Product] queried by
  // [ProductService.GetProduct][google.cloud.retail.v2.ProductService.GetProduct]
  // or
  // [ProductService.ListProducts][google.cloud.retail.v2.ProductService.ListProducts].
  //
  // The returned [Operation][google.longrunning.Operation]s will be obsolete
  // after 1 day, and [GetOperation][google.longrunning.Operations.GetOperation]
  // API will return NOT_FOUND afterwards.
  //
  // If conflicting updates are issued, the
  // [Operation][google.longrunning.Operation]s associated with the stale
  // updates will not be marked as [done][google.longrunning.Operation.done]
  // until being obsolete.
  rpc RemoveFulfillmentPlaces(RemoveFulfillmentPlacesRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v2/{product=projects/*/locations/*/catalogs/*/branches/*/products/**}:removeFulfillmentPlaces"
      body: "*"
    };
    option (google.api.method_signature) = "product";
    option (google.longrunning.operation_info) = {
      response_type: "google.cloud.retail.v2.RemoveFulfillmentPlacesResponse"
      metadata_type: "google.cloud.retail.v2.RemoveFulfillmentPlacesMetadata"
    };
  }

  // Updates local inventory information for a
  // [Product][google.cloud.retail.v2.Product] at a list of places, while
  // respecting the last update timestamps of each inventory field.
  //
  // This process is asynchronous and does not require the
  // [Product][google.cloud.retail.v2.Product] to exist before updating
  // inventory information. If the request is valid, the update will be enqueued
  // and processed downstream. As a consequence, when a response is returned,
  // updates are not immediately manifested in the
  // [Product][google.cloud.retail.v2.Product] queried by
  // [ProductService.GetProduct][google.cloud.retail.v2.ProductService.GetProduct]
  // or
  // [ProductService.ListProducts][google.cloud.retail.v2.ProductService.ListProducts].
  //
  // Local inventory information can only be modified using this method.
  // [ProductService.CreateProduct][google.cloud.retail.v2.ProductService.CreateProduct]
  // and
  // [ProductService.UpdateProduct][google.cloud.retail.v2.ProductService.UpdateProduct]
  // has no effect on local inventories.
  //
  // The returned [Operation][google.longrunning.Operation]s will be obsolete
  // after 1 day, and [GetOperation][google.longrunning.Operations.GetOperation]
  // API will return NOT_FOUND afterwards.
  //
  // If conflicting updates are issued, the
  // [Operation][google.longrunning.Operation]s associated with the stale
  // updates will not be marked as [done][google.longrunning.Operation.done]
  // until being obsolete.
  rpc AddLocalInventories(AddLocalInventoriesRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v2/{product=projects/*/locations/*/catalogs/*/branches/*/products/**}:addLocalInventories"
      body: "*"
    };
    option (google.api.method_signature) = "product";
    option (google.longrunning.operation_info) = {
      response_type: "google.cloud.retail.v2.AddLocalInventoriesResponse"
      metadata_type: "google.cloud.retail.v2.AddLocalInventoriesMetadata"
    };
  }

  // Remove local inventory information for a
  // [Product][google.cloud.retail.v2.Product] at a list of places at a removal
  // timestamp.
  //
  // This process is asynchronous. If the request is valid, the removal will be
  // enqueued and processed downstream. As a consequence, when a response is
  // returned, removals are not immediately manifested in the
  // [Product][google.cloud.retail.v2.Product] queried by
  // [ProductService.GetProduct][google.cloud.retail.v2.ProductService.GetProduct]
  // or
  // [ProductService.ListProducts][google.cloud.retail.v2.ProductService.ListProducts].
  //
  // Local inventory information can only be removed using this method.
  // [ProductService.CreateProduct][google.cloud.retail.v2.ProductService.CreateProduct]
  // and
  // [ProductService.UpdateProduct][google.cloud.retail.v2.ProductService.UpdateProduct]
  // has no effect on local inventories.
  //
  // The returned [Operation][google.longrunning.Operation]s will be obsolete
  // after 1 day, and [GetOperation][google.longrunning.Operations.GetOperation]
  // API will return NOT_FOUND afterwards.
  //
  // If conflicting updates are issued, the
  // [Operation][google.longrunning.Operation]s associated with the stale
  // updates will not be marked as [done][google.longrunning.Operation.done]
  // until being obsolete.
  rpc RemoveLocalInventories(RemoveLocalInventoriesRequest)
      returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v2/{product=projects/*/locations/*/catalogs/*/branches/*/products/**}:removeLocalInventories"
      body: "*"
    };
    option (google.api.method_signature) = "product";
    option (google.longrunning.operation_info) = {
      response_type: "google.cloud.retail.v2.RemoveLocalInventoriesResponse"
      metadata_type: "google.cloud.retail.v2.RemoveLocalInventoriesMetadata"
    };
  }
}

// Request message for
// [ProductService.CreateProduct][google.cloud.retail.v2.ProductService.CreateProduct]
// method.
message CreateProductRequest {
  // Required. The parent catalog resource name, such as
  // `projects/*/locations/global/catalogs/default_catalog/branches/default_branch`.
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "retail.googleapis.com/Branch" }
  ];

  // Required. The [Product][google.cloud.retail.v2.Product] to create.
  Product product = 2 [(google.api.field_behavior) = REQUIRED];

  // Required. The ID to use for the [Product][google.cloud.retail.v2.Product],
  // which will become the final component of the
  // [Product.name][google.cloud.retail.v2.Product.name].
  //
  // If the caller does not have permission to create the
  // [Product][google.cloud.retail.v2.Product], regardless of whether or not it
  // exists, a PERMISSION_DENIED error is returned.
  //
  // This field must be unique among all
  // [Product][google.cloud.retail.v2.Product]s with the same
  // [parent][google.cloud.retail.v2.CreateProductRequest.parent]. Otherwise, an
  // ALREADY_EXISTS error is returned.
  //
  // This field must be a UTF-8 encoded string with a length limit of 128
  // characters. Otherwise, an INVALID_ARGUMENT error is returned.
  string product_id = 3 [(google.api.field_behavior) = REQUIRED];
}

// Request message for
// [ProductService.GetProduct][google.cloud.retail.v2.ProductService.GetProduct]
// method.
message GetProductRequest {
  // Required. Full resource name of [Product][google.cloud.retail.v2.Product],
  // such as
  // `projects/*/locations/global/catalogs/default_catalog/branches/default_branch/products/some_product_id`.
  //
  // If the caller does not have permission to access the
  // [Product][google.cloud.retail.v2.Product], regardless of whether or not it
  // exists, a PERMISSION_DENIED error is returned.
  //
  // If the requested [Product][google.cloud.retail.v2.Product] does not exist,
  // a NOT_FOUND error is returned.
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "retail.googleapis.com/Product" }
  ];
}

// Request message for
// [ProductService.UpdateProduct][google.cloud.retail.v2.ProductService.UpdateProduct]
// method.
message UpdateProductRequest {
  // Required. The product to update/create.
  //
  // If the caller does not have permission to update the
  // [Product][google.cloud.retail.v2.Product], regardless of whether or not it
  // exists, a PERMISSION_DENIED error is returned.
  //
  // If the [Product][google.cloud.retail.v2.Product] to update does not exist
  // and
  // [allow_missing][google.cloud.retail.v2.UpdateProductRequest.allow_missing]
  // is not set, a NOT_FOUND error is returned.
  Product product = 1 [(google.api.field_behavior) = REQUIRED];

  // Indicates which fields in the provided
  // [Product][google.cloud.retail.v2.Product] to update. The immutable and
  // output only fields are NOT supported. If not set, all supported fields (the
  // fields that are neither immutable nor output only) are updated.
  //
  // If an unsupported or unknown field is provided, an INVALID_ARGUMENT error
  // is returned.
  //
  // The attribute key can be updated by setting the mask path as
  // "attributes.${key_name}". If a key name is present in the mask but not in
  // the patching product from the request, this key will be deleted after the
  // update.
  google.protobuf.FieldMask update_mask = 2;

  // If set to true, and the [Product][google.cloud.retail.v2.Product] is not
  // found, a new [Product][google.cloud.retail.v2.Product] will be created. In
  // this situation, `update_mask` is ignored.
  bool allow_missing = 3;
}

// Request message for
// [ProductService.DeleteProduct][google.cloud.retail.v2.ProductService.DeleteProduct]
// method.
message DeleteProductRequest {
  // Required. Full resource name of [Product][google.cloud.retail.v2.Product],
  // such as
  // `projects/*/locations/global/catalogs/default_catalog/branches/default_branch/products/some_product_id`.
  //
  // If the caller does not have permission to delete the
  // [Product][google.cloud.retail.v2.Product], regardless of whether or not it
  // exists, a PERMISSION_DENIED error is returned.
  //
  // If the [Product][google.cloud.retail.v2.Product] to delete does not exist,
  // a NOT_FOUND error is returned.
  //
  // The [Product][google.cloud.retail.v2.Product] to delete can neither be a
  // [Product.Type.COLLECTION][google.cloud.retail.v2.Product.Type.COLLECTION]
  // [Product][google.cloud.retail.v2.Product] member nor a
  // [Product.Type.PRIMARY][google.cloud.retail.v2.Product.Type.PRIMARY]
  // [Product][google.cloud.retail.v2.Product] with more than one
  // [variants][google.cloud.retail.v2.Product.Type.VARIANT]. Otherwise, an
  // INVALID_ARGUMENT error is returned.
  //
  // All inventory information for the named
  // [Product][google.cloud.retail.v2.Product] will be deleted.
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "retail.googleapis.com/Product" }
  ];
}

// Request message for
// [ProductService.ListProducts][google.cloud.retail.v2.ProductService.ListProducts]
// method.
message ListProductsRequest {
  // Required. The parent branch resource name, such as
  // `projects/*/locations/global/catalogs/default_catalog/branches/0`. Use
  // `default_branch` as the branch ID, to list products under the default
  // branch.
  //
  // If the caller does not have permission to list
  // [Product][google.cloud.retail.v2.Product]s under this branch, regardless of
  // whether or not this branch exists, a PERMISSION_DENIED error is returned.
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "retail.googleapis.com/Branch" }
  ];

  // Maximum number of [Product][google.cloud.retail.v2.Product]s to return. If
  // unspecified, defaults to 100. The maximum allowed value is 1000. Values
  // above 1000 will be coerced to 1000.
  //
  // If this field is negative, an INVALID_ARGUMENT error is returned.
  int32 page_size = 2;

  // A page token
  // [ListProductsResponse.next_page_token][google.cloud.retail.v2.ListProductsResponse.next_page_token],
  // received from a previous
  // [ProductService.ListProducts][google.cloud.retail.v2.ProductService.ListProducts]
  // call. Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to
  // [ProductService.ListProducts][google.cloud.retail.v2.ProductService.ListProducts]
  // must match the call that provided the page token. Otherwise, an
  // INVALID_ARGUMENT error is returned.
  string page_token = 3;

  // A filter to apply on the list results. Supported features:
  //
  // * List all the products under the parent branch if
  // [filter][google.cloud.retail.v2.ListProductsRequest.filter] is unset.
  // * List [Product.Type.VARIANT][google.cloud.retail.v2.Product.Type.VARIANT]
  // [Product][google.cloud.retail.v2.Product]s sharing the same
  //   [Product.Type.PRIMARY][google.cloud.retail.v2.Product.Type.PRIMARY]
  //   [Product][google.cloud.retail.v2.Product]. For example:
  //     `primary_product_id = "some_product_id"`
  // * List [Product][google.cloud.retail.v2.Product]s bundled in a
  // [Product.Type.COLLECTION][google.cloud.retail.v2.Product.Type.COLLECTION]
  // [Product][google.cloud.retail.v2.Product].
  //   For example:
  //     `collection_product_id = "some_product_id"`
  // * List [Product][google.cloud.retail.v2.Product]s with a partibular type.
  // For example:
  //     `type = "PRIMARY"`
  //     `type = "VARIANT"`
  //     `type = "COLLECTION"`
  //
  // If the field is unrecognizable, an INVALID_ARGUMENT error is returned.
  //
  // If the specified
  // [Product.Type.PRIMARY][google.cloud.retail.v2.Product.Type.PRIMARY]
  // [Product][google.cloud.retail.v2.Product] or
  // [Product.Type.COLLECTION][google.cloud.retail.v2.Product.Type.COLLECTION]
  // [Product][google.cloud.retail.v2.Product] does not exist, a NOT_FOUND error
  // is returned.
  string filter = 4;

  // The fields of [Product][google.cloud.retail.v2.Product] to return in the
  // responses. If not set or empty, the following fields are returned:
  //
  // * [Product.name][google.cloud.retail.v2.Product.name]
  // * [Product.id][google.cloud.retail.v2.Product.id]
  // * [Product.title][google.cloud.retail.v2.Product.title]
  // * [Product.uri][google.cloud.retail.v2.Product.uri]
  // * [Product.images][google.cloud.retail.v2.Product.images]
  // * [Product.price_info][google.cloud.retail.v2.Product.price_info]
  // * [Product.brands][google.cloud.retail.v2.Product.brands]
  //
  // If "*" is provided, all fields are returned.
  // [Product.name][google.cloud.retail.v2.Product.name] is always returned no
  // matter what mask is set.
  //
  // If an unsupported or unknown field is provided, an INVALID_ARGUMENT error
  // is returned.
  google.protobuf.FieldMask read_mask = 5;
}

// Response message for
// [ProductService.ListProducts][google.cloud.retail.v2.ProductService.ListProducts]
// method.
message ListProductsResponse {
  // The [Product][google.cloud.retail.v2.Product]s.
  repeated Product products = 1;

  // A token that can be sent as
  // [ListProductsRequest.page_token][google.cloud.retail.v2.ListProductsRequest.page_token]
  // to retrieve the next page. If this field is omitted, there are no
  // subsequent pages.
  string next_page_token = 2;
}

// Request message for
// [ProductService.SetInventory][google.cloud.retail.v2.ProductService.SetInventory]
// method.
message SetInventoryRequest {
  // Required. The inventory information to update. The allowable fields to
  // update are:
  //
  // * [Product.price_info][google.cloud.retail.v2.Product.price_info]
  // * [Product.availability][google.cloud.retail.v2.Product.availability]
  // * [Product.available_quantity][google.cloud.retail.v2.Product.available_quantity]
  // * [Product.fulfillment_info][google.cloud.retail.v2.Product.fulfillment_info]
  // The updated inventory fields must be specified in
  // [SetInventoryRequest.set_mask][google.cloud.retail.v2.SetInventoryRequest.set_mask].
  //
  // If
  // [SetInventoryRequest.inventory.name][google.cloud.retail.v2.Product.name]
  // is empty or invalid, an INVALID_ARGUMENT error is returned.
  //
  // If the caller does not have permission to update the
  // [Product][google.cloud.retail.v2.Product] named in
  // [Product.name][google.cloud.retail.v2.Product.name], regardless of whether
  // or not it exists, a PERMISSION_DENIED error is returned.
  //
  // If the [Product][google.cloud.retail.v2.Product] to update does not have
  // existing inventory information, the provided inventory information will be
  // inserted.
  //
  // If the [Product][google.cloud.retail.v2.Product] to update has existing
  // inventory information, the provided inventory information will be merged
  // while respecting the last update time for each inventory field, using the
  // provided or default value for
  // [SetInventoryRequest.set_time][google.cloud.retail.v2.SetInventoryRequest.set_time].
  //
  // The caller can replace place IDs for a subset of fulfillment types in the
  // following ways:
  //
  // * Adds "fulfillment_info" in
  // [SetInventoryRequest.set_mask][google.cloud.retail.v2.SetInventoryRequest.set_mask]
  // * Specifies only the desired fulfillment types and corresponding place IDs
  // to update in
  // [SetInventoryRequest.inventory.fulfillment_info][google.cloud.retail.v2.Product.fulfillment_info]
  //
  // The caller can clear all place IDs from a subset of fulfillment types in
  // the following ways:
  //
  // * Adds "fulfillment_info" in
  // [SetInventoryRequest.set_mask][google.cloud.retail.v2.SetInventoryRequest.set_mask]
  // * Specifies only the desired fulfillment types to clear in
  // [SetInventoryRequest.inventory.fulfillment_info][google.cloud.retail.v2.Product.fulfillment_info]
  // * Checks that only the desired fulfillment info types have empty
  // [SetInventoryRequest.inventory.fulfillment_info.place_ids][google.cloud.retail.v2.FulfillmentInfo.place_ids]
  //
  // The last update time is recorded for the following inventory fields:
  // * [Product.price_info][google.cloud.retail.v2.Product.price_info]
  // * [Product.availability][google.cloud.retail.v2.Product.availability]
  // * [Product.available_quantity][google.cloud.retail.v2.Product.available_quantity]
  // * [Product.fulfillment_info][google.cloud.retail.v2.Product.fulfillment_info]
  //
  // If a full overwrite of inventory information while ignoring timestamps is
  // needed,
  // [ProductService.UpdateProduct][google.cloud.retail.v2.ProductService.UpdateProduct]
  // should be invoked instead.
  Product inventory = 1 [(google.api.field_behavior) = REQUIRED];

  // Indicates which inventory fields in the provided
  // [Product][google.cloud.retail.v2.Product] to update.
  //
  // At least one field must be provided.
  //
  // If an unsupported or unknown field is provided, an INVALID_ARGUMENT error
  // is returned and the entire update will be ignored.
  google.protobuf.FieldMask set_mask = 2;

  // The time when the request is issued, used to prevent
  // out-of-order updates on inventory fields with the last update time
  // recorded. If not provided, the internal system time will be used.
  google.protobuf.Timestamp set_time = 3;

  // If set to true, and the [Product][google.cloud.retail.v2.Product] with name
  // [Product.name][google.cloud.retail.v2.Product.name] is not found, the
  // inventory update will still be processed and retained for at most 1 day
  // until the [Product][google.cloud.retail.v2.Product] is created. If set to
  // false, a NOT_FOUND error is returned if the
  // [Product][google.cloud.retail.v2.Product] is not found.
  bool allow_missing = 4;
}

// Metadata related to the progress of the SetInventory operation.
// Currently empty because there is no meaningful metadata populated from the
// [ProductService.SetInventory][google.cloud.retail.v2.ProductService.SetInventory]
// method.
message SetInventoryMetadata {}

// Response of the SetInventoryRequest.  Currently empty because
// there is no meaningful response populated from the
// [ProductService.SetInventory][google.cloud.retail.v2.ProductService.SetInventory]
// method.
message SetInventoryResponse {}

// Request message for
// [ProductService.AddFulfillmentPlaces][google.cloud.retail.v2.ProductService.AddFulfillmentPlaces]
// method.
message AddFulfillmentPlacesRequest {
  // Required. Full resource name of [Product][google.cloud.retail.v2.Product],
  // such as
  // `projects/*/locations/global/catalogs/default_catalog/branches/default_branch/products/some_product_id`.
  //
  // If the caller does not have permission to access the
  // [Product][google.cloud.retail.v2.Product], regardless of whether or not it
  // exists, a PERMISSION_DENIED error is returned.
  string product = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "retail.googleapis.com/Product" }
  ];

  // Required. The fulfillment type, including commonly used types (such as
  // pickup in store and same day delivery), and custom types.
  //
  // Supported values:
  //
  // * "pickup-in-store"
  // * "ship-to-store"
  // * "same-day-delivery"
  // * "next-day-delivery"
  // * "custom-type-1"
  // * "custom-type-2"
  // * "custom-type-3"
  // * "custom-type-4"
  // * "custom-type-5"
  //
  // If this field is set to an invalid value other than these, an
  // INVALID_ARGUMENT error is returned.
  //
  // This field directly corresponds to
  // [Product.fulfillment_info.type][google.cloud.retail.v2.FulfillmentInfo.type].
  string type = 2 [(google.api.field_behavior) = REQUIRED];

  // Required. The IDs for this
  // [type][google.cloud.retail.v2.AddFulfillmentPlacesRequest.type], such as
  // the store IDs for "pickup-in-store" or the region IDs for
  // "same-day-delivery" to be added for this
  // [type][google.cloud.retail.v2.AddFulfillmentPlacesRequest.type]. Duplicate
  // IDs will be automatically ignored.
  //
  // At least 1 value is required, and a maximum of 2000 values are allowed.
  // Each value must be a string with a length limit of 10 characters, matching
  // the pattern `[a-zA-Z0-9_-]+`, such as "store1" or "REGION-2". Otherwise, an
  // INVALID_ARGUMENT error is returned.
  //
  // If the total number of place IDs exceeds 2000 for this
  // [type][google.cloud.retail.v2.AddFulfillmentPlacesRequest.type] after
  // adding, then the update will be rejected.
  repeated string place_ids = 3 [(google.api.field_behavior) = REQUIRED];

  // The time when the fulfillment updates are issued, used to prevent
  // out-of-order updates on fulfillment information. If not provided, the
  // internal system time will be used.
  google.protobuf.Timestamp add_time = 4;

  // If set to true, and the [Product][google.cloud.retail.v2.Product] is not
  // found, the fulfillment information will still be processed and retained for
  // at most 1 day and processed once the
  // [Product][google.cloud.retail.v2.Product] is created. If set to false, a
  // NOT_FOUND error is returned if the
  // [Product][google.cloud.retail.v2.Product] is not found.
  bool allow_missing = 5;
}

// Metadata related to the progress of the AddFulfillmentPlaces operation.
// Currently empty because there is no meaningful metadata populated from the
// [ProductService.AddFulfillmentPlaces][google.cloud.retail.v2.ProductService.AddFulfillmentPlaces]
// method.
message AddFulfillmentPlacesMetadata {}

// Response of the AddFulfillmentPlacesRequest.  Currently empty because
// there is no meaningful response populated from the
// [ProductService.AddFulfillmentPlaces][google.cloud.retail.v2.ProductService.AddFulfillmentPlaces]
// method.
message AddFulfillmentPlacesResponse {}

// Request message for
// [ProductService.AddLocalInventories][google.cloud.retail.v2.ProductService.AddLocalInventories]
// method.
message AddLocalInventoriesRequest {
  // Required. Full resource name of [Product][google.cloud.retail.v2.Product],
  // such as
  // `projects/*/locations/global/catalogs/default_catalog/branches/default_branch/products/some_product_id`.
  //
  // If the caller does not have permission to access the
  // [Product][google.cloud.retail.v2.Product], regardless of whether or not it
  // exists, a PERMISSION_DENIED error is returned.
  string product = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "retail.googleapis.com/Product" }
  ];

  // Required. A list of inventory information at difference places. Each place
  // is identified by its place ID. At most 3000 inventories are allowed per
  // request.
  repeated LocalInventory local_inventories = 2
      [(google.api.field_behavior) = REQUIRED];

  // Indicates which inventory fields in the provided list of
  // [LocalInventory][google.cloud.retail.v2.LocalInventory] to update. The
  // field is updated to the provided value.
  //
  // If a field is set while the place does not have a previous local inventory,
  // the local inventory at that store is created.
  //
  // If a field is set while the value of that field is not provided, the
  // original field value, if it exists, is deleted.
  //
  // If the mask is not set or set with empty paths, all inventory fields will
  // be updated.
  //
  // If an unsupported or unknown field is provided, an INVALID_ARGUMENT error
  // is returned and the entire update will be ignored.
  google.protobuf.FieldMask add_mask = 4;

  // The time when the inventory updates are issued. Used to prevent
  // out-of-order updates on local inventory fields. If not provided, the
  // internal system time will be used.
  google.protobuf.Timestamp add_time = 5;

  // If set to true, and the [Product][google.cloud.retail.v2.Product] is not
  // found, the local inventory will still be processed and retained for at most
  // 1 day and processed once the [Product][google.cloud.retail.v2.Product] is
  // created. If set to false, a NOT_FOUND error is returned if the
  // [Product][google.cloud.retail.v2.Product] is not found.
  bool allow_missing = 6;
}

// Metadata related to the progress of the AddLocalInventories operation.
// Currently empty because there is no meaningful metadata populated from the
// [ProductService.AddLocalInventories][google.cloud.retail.v2.ProductService.AddLocalInventories]
// method.
message AddLocalInventoriesMetadata {}

// Response of the
// [ProductService.AddLocalInventories][google.cloud.retail.v2.ProductService.AddLocalInventories]
// API.  Currently empty because there is no meaningful response populated from
// the
// [ProductService.AddLocalInventories][google.cloud.retail.v2.ProductService.AddLocalInventories]
// method.
message AddLocalInventoriesResponse {}

// Request message for
// [ProductService.RemoveLocalInventories][google.cloud.retail.v2.ProductService.RemoveLocalInventories]
// method.
message RemoveLocalInventoriesRequest {
  // Required. Full resource name of [Product][google.cloud.retail.v2.Product],
  // such as
  // `projects/*/locations/global/catalogs/default_catalog/branches/default_branch/products/some_product_id`.
  //
  // If the caller does not have permission to access the
  // [Product][google.cloud.retail.v2.Product], regardless of whether or not it
  // exists, a PERMISSION_DENIED error is returned.
  string product = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "retail.googleapis.com/Product" }
  ];

  // Required. A list of place IDs to have their inventory deleted.
  // At most 3000 place IDs are allowed per request.
  repeated string place_ids = 2 [(google.api.field_behavior) = REQUIRED];

  // The time when the inventory deletions are issued. Used to prevent
  // out-of-order updates and deletions on local inventory fields. If not
  // provided, the internal system time will be used.
  google.protobuf.Timestamp remove_time = 5;

  // If set to true, and the [Product][google.cloud.retail.v2.Product] is not
  // found, the local inventory removal request will still be processed and
  // retained for at most 1 day and processed once the
  // [Product][google.cloud.retail.v2.Product] is created. If set to false, a
  // NOT_FOUND error is returned if the
  // [Product][google.cloud.retail.v2.Product] is not found.
  bool allow_missing = 3;
}

// Metadata related to the progress of the RemoveLocalInventories operation.
// Currently empty because there is no meaningful metadata populated from the
// [ProductService.RemoveLocalInventories][google.cloud.retail.v2.ProductService.RemoveLocalInventories]
// method.
message RemoveLocalInventoriesMetadata {}

// Response of the
// [ProductService.RemoveLocalInventories][google.cloud.retail.v2.ProductService.RemoveLocalInventories]
// API.  Currently empty because there is no meaningful response populated from
// the
// [ProductService.RemoveLocalInventories][google.cloud.retail.v2.ProductService.RemoveLocalInventories]
// method.
message RemoveLocalInventoriesResponse {}

// Request message for
// [ProductService.RemoveFulfillmentPlaces][google.cloud.retail.v2.ProductService.RemoveFulfillmentPlaces]
// method.
message RemoveFulfillmentPlacesRequest {
  // Required. Full resource name of [Product][google.cloud.retail.v2.Product],
  // such as
  // `projects/*/locations/global/catalogs/default_catalog/branches/default_branch/products/some_product_id`.
  //
  // If the caller does not have permission to access the
  // [Product][google.cloud.retail.v2.Product], regardless of whether or not it
  // exists, a PERMISSION_DENIED error is returned.
  string product = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "retail.googleapis.com/Product" }
  ];

  // Required. The fulfillment type, including commonly used types (such as
  // pickup in store and same day delivery), and custom types.
  //
  // Supported values:
  //
  // * "pickup-in-store"
  // * "ship-to-store"
  // * "same-day-delivery"
  // * "next-day-delivery"
  // * "custom-type-1"
  // * "custom-type-2"
  // * "custom-type-3"
  // * "custom-type-4"
  // * "custom-type-5"
  //
  // If this field is set to an invalid value other than these, an
  // INVALID_ARGUMENT error is returned.
  //
  // This field directly corresponds to
  // [Product.fulfillment_info.type][google.cloud.retail.v2.FulfillmentInfo.type].
  string type = 2 [(google.api.field_behavior) = REQUIRED];

  // Required. The IDs for this
  // [type][google.cloud.retail.v2.RemoveFulfillmentPlacesRequest.type], such as
  // the store IDs for "pickup-in-store" or the region IDs for
  // "same-day-delivery", to be removed for this
  // [type][google.cloud.retail.v2.RemoveFulfillmentPlacesRequest.type].
  //
  // At least 1 value is required, and a maximum of 2000 values are allowed.
  // Each value must be a string with a length limit of 10 characters, matching
  // the pattern `[a-zA-Z0-9_-]+`, such as "store1" or "REGION-2". Otherwise, an
  // INVALID_ARGUMENT error is returned.
  repeated string place_ids = 3 [(google.api.field_behavior) = REQUIRED];

  // The time when the fulfillment updates are issued, used to prevent
  // out-of-order updates on fulfillment information. If not provided, the
  // internal system time will be used.
  google.protobuf.Timestamp remove_time = 4;

  // If set to true, and the [Product][google.cloud.retail.v2.Product] is not
  // found, the fulfillment information will still be processed and retained for
  // at most 1 day and processed once the
  // [Product][google.cloud.retail.v2.Product] is created. If set to false, a
  // NOT_FOUND error is returned if the
  // [Product][google.cloud.retail.v2.Product] is not found.
  bool allow_missing = 5;
}

// Metadata related to the progress of the RemoveFulfillmentPlaces operation.
// Currently empty because there is no meaningful metadata populated from the
// [ProductService.RemoveFulfillmentPlaces][google.cloud.retail.v2.ProductService.RemoveFulfillmentPlaces]
// method.
message RemoveFulfillmentPlacesMetadata {}

// Response of the RemoveFulfillmentPlacesRequest. Currently empty because there
// is no meaningful response populated from the
// [ProductService.RemoveFulfillmentPlaces][google.cloud.retail.v2.ProductService.RemoveFulfillmentPlaces]
// method.
message RemoveFulfillmentPlacesResponse {}
