Exemplo de Criação de um Código de Pedido do Batch

Este exemplo mostra como criar um pedido em lote que pode conter milhares de operações de inserção, obtenção e eliminação.

Para obter informações sobre as classes relacionadas com o lote utilizadas por este exemplo, veja Batch, Item e BatchItemError. Para obter informações sobre como utilizar a funcionalidade batch, veja Utilizar o processamento em lotes.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using System.Net;
using Content.OAuth;
using Newtonsoft.Json;           // NuGet Json.NET

namespace Batch
{
    class Program
    {
        // The client ID and secret that you were given when you
        // registered your application.

        private static string clientId = "<CLIENTIDGOESHERE>";
        private static string clientSecret = "<CLIENTSECRETGOESHERE>";
        private static string storedRefreshToken = "<REFRESHTOKENGOESHERE OR NULL";
        private static CodeGrantOauth tokens = null;
        private static DateTime tokenExpiration;

        private static string devToken = "<DEVELOPERTOKENGOESHERE>";

        // URI templates used to get resources.

        public const string BaseUri = "https://content.api.bingads.microsoft.com/shopping/v9.1";
        public static string BmcUri = BaseUri + "/bmc/{0}";
        public static string BatchUri = BmcUri + "/products/batch";

        // Query strings

        public static string queryString = "?bmc-catalog-id={1}&alt=json";

        // Replace with your store ID and catalog ID.

        public static long merchantId = <STOREIDGOESHERE>;
        public static long catalogId = <CATALOGIDGOESHERE>;

        // Used in example to store operation ids.
        
        public static List<string> ProductList = new List<string>();
        public static int batchOperationId = 0;

        // The maximum number of objects that you can specify depends upon
        // the number of product attributes that you specify and whether you 
        // compress the data. If you do not compress the data, 
        // you can specify about 2,000 objects; otherwise, you can specify 
        // about 12,000 objects.
 
        // For this example, limit the list to 5 products.

        public const int MAX_ENTRIES = 5;


        static void Main(string[] args)
        {
            try
            {
                var headers = GetCredentialHeaders();

                // Build URL with query string and store id.

                var url = string.Format(BatchUri + queryString, merchantId, catalogId);

                // A batch operation may include any operation (insert, get, delete);
                // however, because the operations may not act on the same product,
                // this example performs the batch operations separately.

                BatchInsert(url, headers);
                BatchGet(url, headers);
                BatchDelete(url, headers);
            }
            catch (WebException e)
            {
                Console.WriteLine("\n" + e.Message);

                HttpWebResponse response = (HttpWebResponse)e.Response;

                // If the request is bad, the API returns the errors in the 
                // body of the request. For cases where the path may be valid, but
                // the resource does not belong to the user, the API returns not found.

                if (response != null &&
                    (HttpStatusCode.BadRequest == response.StatusCode ||
                     HttpStatusCode.NotFound == response.StatusCode ||
                     HttpStatusCode.InternalServerError == response.StatusCode))
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        StreamReader reader = new StreamReader(stream);
                        string json = reader.ReadToEnd();
                        reader.Close();

                        try
                        {
                            var errors = JsonConvert.DeserializeObject<ContentError>(json);

                            PrintErrors(errors.Error.Errors);
                        }
                        catch (Exception deserializeError)
                        {
                            // This case occurs when the path is not valid.

                            if (HttpStatusCode.NotFound == response.StatusCode)
                            {
                                Console.WriteLine("Path not found: " + response.ResponseUri);
                            }
                            else
                            {
                                Console.WriteLine(deserializeError.Message);
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("\n" + e.Message);
            }

        }

        // Use the BatchCollection object and POST operation to create multiple Content API operations.
        // This example uses compression.
        
        private static BatchCollection BatchRequest(string uri, WebHeaderCollection headers, BatchCollection batchCollection)
        {
            var request = (HttpWebRequest)WebRequest.Create(uri);
            request.Method = "POST";
            request.Headers = headers;
            request.Headers.Add("Content-Encoding", "gzip");
            request.AutomaticDecompression = DecompressionMethods.GZip;
            request.ContentType = "application/json";

            var serializerSettings = new JsonSerializerSettings();
            serializerSettings.NullValueHandling = NullValueHandling.Ignore;

            var json = JsonConvert.SerializeObject(batchCollection, serializerSettings);

            using (Stream requestStream = request.GetRequestStream())
            {
                var buffer = Encoding.UTF8.GetBytes(json);

                using (GZipStream compressionStream = new GZipStream(requestStream, CompressionMode.Compress, true))
                {
                    compressionStream.Write(buffer, 0, buffer.Length);
                }
            }

            var response = (HttpWebResponse)request.GetResponse();
            BatchCollection batchOut = null;

            using (Stream responseStream = response.GetResponseStream())
            {
                var reader = new StreamReader(responseStream);
                var jsonOut = reader.ReadToEnd();
                reader.Close();
                batchOut = JsonConvert.DeserializeObject<BatchCollection>(jsonOut);
            }

            return batchOut;
        }

        // Add 5 products to the catalog. Write the IDs to a list that's used in BatchGet
        // and BatchDelete.

        private static void BatchInsert(string url, WebHeaderCollection headers)
        {
            var batchCollection = new BatchCollection();
            batchCollection.Entries = new List<BatchEntry>();

            for (var i = 0; i < MAX_ENTRIES; i++)
            {
                var product = GetProduct();
                product.OfferId = i.ToString();
                var batchEntry = new BatchEntry(i, merchantId, "insert", null, product);
                batchCollection.Entries.Add(batchEntry);
            }

            // If you try to insert too many products, the call fails with status code 413.

            var batchOut = BatchRequest(url, headers, batchCollection);

            for (var i = 0; i < batchOut.Entries.Count; i++)
            {
                if (batchOut.Entries[i].Error != null)
                {
                    Console.WriteLine("Failed to insert product {0}.", 
                        batchCollection.Entries[(int)(batchOut.Entries[i].BatchId)].Product.OfferId);
                    PrintErrors(batchOut.Entries[i].Error.Errors);
                }
                else
                {
                    Console.WriteLine("Inserted product " + batchOut.Entries[i].Product.Id);
                    ProductList.Add(batchOut.Entries[i].Product.Id);
                }
            }
        }

        // Use GET operation on the products inserted by BatchInsert.

        private static void BatchGet(string url, WebHeaderCollection headers)
        {
            var batchCollection = new BatchCollection();
            batchCollection.Entries = new List<BatchEntry>();
            batchOperationId = 0;

            foreach (string productId in ProductList)
            {
                var batchEntry = new BatchEntry(batchOperationId++, merchantId, "get", productId, null);
                batchCollection.Entries.Add(batchEntry);
            }

            var batchOut = BatchRequest(url, headers, batchCollection);

            for (var i = 0; i < batchOut.Entries.Count; i++)
            {
                if (batchOut.Entries[i].Error != null)
                {
                    Console.WriteLine("Failed to get product {0}.", 
                        batchCollection.Entries[(int)(batchOut.Entries[i].BatchId)].ProductId);
                    PrintErrors(batchOut.Entries[i].Error.Errors);
                }
                else
                {
                    if (batchOut.Entries[i].Product == null)
                    {
                        Console.WriteLine("Product {0} was not found.",
                            batchCollection.Entries[(int)(batchOut.Entries[i].BatchId)].ProductId);
                    }
                    else
                    {
                        PrintProduct(batchOut.Entries[i].Product);
                        Console.WriteLine();
                    }
                }
            }
        }

        // Use DELETE operation on the products inserted in BatchInsert.

        private static void BatchDelete(string url, WebHeaderCollection headers)
        {
            var batchCollection = new BatchCollection();
            batchCollection.Entries = new List<BatchEntry>();
            batchOperationId = 0;

            foreach (string productId in ProductList)
            {
                var batchEntry = new BatchEntry(batchOperationId++, merchantId, "delete", productId, null);
                batchCollection.Entries.Add(batchEntry);
            }

            var batchOut = BatchRequest(url, headers, batchCollection);

            for (var i = 0; i < batchOut.Entries.Count; i++)
            {
                if (batchOut.Entries[i].Error != null)
                {
                    Console.WriteLine("Failed to delete product {0}.", 
                        batchCollection.Entries[(int)(batchOut.Entries[i].BatchId)].ProductId);
                    PrintErrors(batchOut.Entries[i].Error.Errors);
                }
                else
                {
                    Console.WriteLine("Deleted product " + batchCollection.Entries[i].ProductId);
                }
            }
        }


        // This example creates a single product that it adds to the store.
        // The calling code overwrites offerId to ensure a new product is added.

        private static Product GetProduct()
        {
            var product = new Product()
            {
                // The following properties are required.

                Availability = "in stock",
                Channel = Channel.Online.ToString(),
                Condition = Condition.New.ToString(),
                ContentLanguage = "en",
                Link = "http://www.contoso.com/apparel/men/tshirts.htm",
                ImageLink = "http://www.contoso.com/pics/tees.jpg",
                OfferId = "1",
                Price = new Price()
                {
                    Currency = "USD",
                    Value = (decimal)12.00
                },
                TargetCountry = "US",
                Title = "Mens T-Shirt",

                // If the manufacturer provides identifiers for brand, gtin
                // or mpn, you must set those fields, and set identifierExists
                // to true.
                
                IdentifierExists = false;
                
                // The following properties are optional.

                ExpirationDate = DateTime.UtcNow.AddDays(45).ToString("O")  // "2016-06-03T20:47:27.0437485Z"

                //SalePriceEffectiveDate = string.Format("{0:O}/{1:O}", 
                //    DateTime.UtcNow, DateTime.UtcNow.AddDays(10)),
                //ProductType = "Sporting Goods > Mens Shirts > Mens T-Shirts",
            };

            return product;
        }


        // Print out some field of the product offer.
        
        private static void PrintProduct(Product product)
        {
            Console.WriteLine("qualifiedProductId: " + product.Id);
            Console.WriteLine("channel : " + product.Channel);
            Console.WriteLine("language: " + product.ContentLanguage);
            Console.WriteLine("country: " + product.TargetCountry);
            Console.WriteLine("productId: " + product.OfferId);
            Console.WriteLine("expiry date: " + product.ExpirationDate);

            Console.WriteLine("title: " + product.Title);

            if (product.Price != null)
            {
                Console.WriteLine("price: {0} {1}", product.Price.Value, product.Price.Currency);
            }
        }

        // Print errors.
        
        private static void PrintErrors(List<Error> errors)
        {
            foreach (Error error in errors)
            {
                Console.WriteLine("reason: {0}\nmessage: {1}\nlocation type: {2}\nlocation: {3}\n",
                    error.Reason, error.Message, error.LocationType, error.Location);
            }
        }


        // Gets the AuthenticationToken and DeveloperToken headers 
        // that are required to call the API. This example use OAuth
        // instead of using the legacy credentials (username and password).

        private static WebHeaderCollection GetCredentialHeaders()
        {
            // TODO: Add logic to get the logged on user's refresh token 
            // from secured storage. 

            tokens = GetOauthTokens(storedRefreshToken);

            var headers = new WebHeaderCollection();
            headers.Add("AuthenticationToken", tokens.AccessToken);
            headers.Add("DeveloperToken", devToken);

            return headers;
        }

        // Gets the OAuth tokens. If the refresh token doesn't exist, get 
        // the user's consent and a new access and refresh token.
        private static CodeGrantOauth GetOauthTokens(string refreshToken)
        {
            CodeGrantOauth auth = null;

            if (string.IsNullOrEmpty(refreshToken))
            {
                auth = new CodeGrantOauth(clientId, clientSecret);
                auth.GetAccessToken();
            }
            else
            {
                auth = new CodeGrantOauth(clientId, clientSecret);
                auth.RefreshAccessToken(refreshToken);

                // Refresh tokens can become invalid for several reasons
                // such as the user's password changed.

                if (!string.IsNullOrEmpty(auth.Error))
                {
                    auth = GetOauthTokens(null);
                }
            }

            // TODO: Store the new refresh token in secured storage
            // for the logged on user.

            storedRefreshToken = auth.RefreshToken;
            tokenExpiration = DateTime.Now.AddSeconds(auth.Expiration);

            return auth;
        }

    }

    // Defines the BMC classes.

    class BatchCollection
    {
        [JsonProperty("entries")]
        public List<BatchEntry> Entries { get; set; }
    }

    class BatchEntry
    {
        public BatchEntry() { }

        public BatchEntry(int batchId, long merchantId, string method, string productId, Product product)
        {
            this.BatchId = batchId;
            this.MerchantId = merchantId;
            this.Method = method;
            this.ProductId = productId;
            this.Product = product;
        }

        [JsonProperty("batchId")]
        public long BatchId { get; set; }

        [JsonProperty("merchantId")]
        public long MerchantId { get; set; }

        [JsonProperty("method")]
        public string Method { get; set; }

        [JsonProperty("productId")]
        public string ProductId { get; set; }

        [JsonProperty("product")]
        public Product Product { get; set; }

        [JsonProperty("errors")]
        public BatchItemError Error { get; set; }
    }


    // Defines a product resource.
    
    public class Product
    {
        public Product() { }

        [JsonProperty("additionalImageLinks")]
        public List<string> AdditionalImageLinks { get; set; }

        [JsonProperty("adult")]
        public bool? IsAdult { get; set; }

        [JsonProperty("adwordsGrouping")]
        public string AdwordsGrouping { get; set; }

        [JsonProperty("adwordsLabels")]
        public List<string> AdwordsLabels { get; set; }

        [JsonProperty("adwordsRedirect")]
        public string AdwordsRedirect { get; set; }

        [JsonProperty("ageGroup")]
        public string AgeGroup { get; set; }

        [JsonProperty("availability")]
        public string Availability { get; set; }

        [JsonProperty("availabilityDate")]
        public string AvailabilityDate { get; set; }

        [JsonProperty("brand")]
        public string Brand { get; set; }

        [JsonProperty("channel")]
        public string Channel { get; set; }

        [JsonProperty("color")]
        public string Color { get; set; }

        [JsonProperty("condition")]
        public string Condition { get; set; }


        [JsonProperty("contentLanguage")]
        public string ContentLanguage { get; set; }

        [JsonProperty("customAttributes")]
        public List<ProductCustomAttribute> CustomAttributes { get; set; }

        [JsonProperty("customGroups")]
        public List<ProductCustomGroup> CustomGroups { get; set; }

        [JsonProperty("customLabel0")]
        public string CustomLabel0 { get; set; }

        [JsonProperty("customLabel1")]
        public string CustomLabel1 { get; set; }

        [JsonProperty("customLabel2")]
        public string CustomLabel2 { get; set; }

        [JsonProperty("customLabel3")]
        public string CustomLabel3 { get; set; }

        [JsonProperty("customLabel4")]
        public string CustomLabel4 { get; set; }

        [JsonProperty("description")]
        public string Description { get; set; }

        [JsonProperty("destinations")]
        public List<ProductDestination> Destinations { get; set; }

        [JsonProperty("energyEfficiencyClass")]
        public string EnergyEfficiencyClass { get; set; }

        [JsonProperty("expirationDate")]
        public string ExpirationDate { get; set; }

        [JsonProperty("gender")]
        public string Gender { get; set; }

        [JsonProperty("googleProductCategory")]
        public string GoogleProductCategory { get; set; }

        [JsonProperty("gtin")]
        public string Gtin { get; set; }

        [JsonProperty("id")]
        public string Id { get; set; }

        [JsonProperty("identifierExists")]
        public bool? IdentifierExists { get; set; }

        [JsonProperty("imageLink")]
        public string ImageLink { get; set; }

        [JsonProperty("isBundle")]
        public bool? IsBundle { get; set; }

        [JsonProperty("itemGroupId")]
        public string ItemGroupId { get; set; }

        [JsonProperty("kind")] ////For example: "kind":"content#product"
        public string Kind { get; set; }

        [JsonProperty("link")]
        public string Link { get; set; }

        [JsonProperty("material")]
        public string Material { get; set; }

        [JsonProperty("multipack")]
        public ulong? MerchantMultipackQuantity { get; set; }

        [JsonProperty("mobileLink")]
        public string MobileLink { get; set; }

        [JsonProperty("mpn")]
        public string Mpn { get; set; }

        [JsonProperty("offerId")]
        public string OfferId { get; set; }

        [JsonProperty("onlineOnly")]
        public bool? OnlineOnly { get; set; }

        [JsonProperty("pattern")]
        public string Pattern { get; set; }

        [JsonProperty("price")]
        public Price Price { get; set; }

        [JsonProperty("productType")]
        public string ProductType { get; set; }

        [JsonProperty("salePrice")]
        public Price SalePrice { get; set; }

        [JsonProperty("salePriceEffectiveDate")]
        public string SalePriceEffectiveDate { get; set; }

        [JsonProperty("shipping")]
        public List<ProductShipping> Shipping { get; set; }

        [JsonProperty("shippingWeight")]
        public ProductShippingWeight ShippingWeight { get; set; }

        [JsonProperty("shippingLabel")]
        public string ShippingLabel { get; set; }

        [JsonProperty("sizes")]
        public List<string> Sizes { get; set; }

        [JsonProperty("sizeSystem")]
        public string SizeSystem { get; set; }

        [JsonProperty("sizeType")]
        public string SizeType { get; set; }

        [JsonProperty("targetCountry")]
        public string TargetCountry { get; set; }

        [JsonProperty("taxes")]
        public List<ProductTax> Taxes { get; set; }

        [JsonProperty("title")]
        public string Title { get; set; }

        [JsonProperty("unitPricingBaseMeasure")]
        public UnitPricing UnitPricingBaseMeasure { get; set; }

        [JsonProperty("unitPricingMeasure")]
        public UnitPricing UnitPricingMeasure { get; set; }

        [JsonProperty("validatedDestinations")]
        public List<string> ValidatedDestinations { get; set; }
    }

    // Defines a product's price.
    
    public class Price
    {
        [JsonProperty("currency")]
        public string Currency { get; set; }

        [JsonProperty("value")]
        public decimal? Value { get; set; }
    }

    // Defines a product's tax.
    
    public class ProductTax
    {
        [JsonProperty("country")]
        public string Country { get; set; }

        [JsonProperty("locationId")]
        public long? LocationId { get; set; }

        [JsonProperty("postalCode")]
        public string PostalCode { get; set; }

        [JsonProperty("rate")]
        public double? Rate { get; set; }

        [JsonProperty("region")]
        public string Region { get; set; }

        [JsonProperty("taxShip")]
        public bool? TaxShip { get; set; }
    }

    // Defines a product's shipping weight.
    
    public class ProductShippingWeight
    {
        [JsonProperty("unit")]
        public string Unit { get; set; }

        [JsonProperty("value")]
        public double? Value { get; set; }
    }

    // Defines a product's shipping cost.
    
    public class ProductShipping
    {
        [JsonProperty("country")]
        public string Country { get; set; }

        [JsonProperty("locationGroupName")]
        public string LocationGroupName { get; set; }

        [JsonProperty("locationId")]
        public long? LocationId { get; set; }

        [JsonProperty("postalCode")]
        public string PostalCode { get; set; }

        [JsonProperty("price")]
        public Price Price { get; set; }

        [JsonProperty("region")]
        public string Region { get; set; }

        [JsonProperty("service")]
        public string Service { get; set; }
    }

    // Defines per unit pricing.
    
    public class UnitPricing
    {
        [JsonProperty("unit")]
        public string Unit { get; set; }

        [JsonProperty("value")]
        public double Value{ get; set; }
    }

    // Defines a destination.
    
    public class ProductDestination
    {
        [JsonProperty("destinationName")]
        public string DestinationName { get; set; }

        [JsonProperty("intention")]
        public string Intention { get; set; }
    }

    // Defines a product's custom attribute.
    
    public class ProductCustomAttribute
    {
        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("type")]
        public string Type { get; set; }

        [JsonProperty("unit")]
        public string Unit { get; set; }

        [JsonProperty("value")]
        public string Value { get; set; }
    }

    // Defines a custom group of attributes.
    
    public class ProductCustomGroup
    {
        [JsonProperty("customGroups")]
        public List<ProductCustomAttribute> Attributes { get; set; }

        [JsonProperty("name")]
        public string Name { get; set; }
    }

    // Defines the BMC enumerations.

    public enum Channel
    {
        Online,
        Local
    }

    public enum AgeGroup
    {
        Adult,
        Kids,
        Toddler,
        Infant,
        Newborn
    }

    public enum Condition
    {
        New,
        Refurbished,
        Used
    }

    public enum Gender
    {
        Female,
        Male,
        Unisex
    }

    public enum ApparelCut
    {
        Regular,
        Maternity,
        Petite,
        Oversize
    }

    public enum Intention
    {
        Default,
        Excluded,
        Optional,
        Required
    }

    public enum AttributeType
    {
        Boolean,
        DateTimeRange,
        Float,
        Group,
        Int,
        Price,
        Text,
        Time,
        URL
    }

    public enum AttributeUnit
    {
        Int,
        Float
    }

    // Classes used to handle errors.
    
    public class Error
    {
        [JsonProperty("location")]
        public string Location { get; set; }

        [JsonProperty("locationType")]
        public string LocationType { get; set; }

        [JsonProperty("domain")]
        public string Domain { get; set; }

        [JsonProperty("message")]
        public string Message { get; set; }

        [JsonProperty("reason")]
        public string Reason { get; set; }
    }

    public class ErrorCollection
    {
        [JsonProperty("code")]
        public string Code { get; set; }

        [JsonProperty("errors")]
        public List<Error> Errors { get; set; }

        [JsonProperty("message")]
        public string Message { get; set; }

    }

    public class ContentError
    {
        [JsonProperty("error")]
        public ErrorCollection Error { get; set; }
    }

    public class BatchItemError
    {
        [JsonProperty("errors")]
        public List<Error> Errors { get; set; }
    }

}
package products.capi.microsoft.com;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.zip.*;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

// Uses Jackson to serialize and deserialize JSON.

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.*;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonInclude.Include;


class ProductsEx {

    // Maps the product's availability strings to enum values.
    
	public static Map<ProductAvailability, String> Availability = new HashMap<ProductAvailability, String>();
	static {
		Availability.put(ProductAvailability.InStock, "in stock");
		Availability.put(ProductAvailability.OutOfStock, "out of stock");
		Availability.put(ProductAvailability.PreOrder, "preorder");
	}
	
	// Jackson object used to serialize and deserialize JSON.

	private static ObjectMapper jsonMapper = new ObjectMapper();

	private static String accessToken = "<accesstokengoeshere>";
	
    public static String devToken = "<devtokengoeshere>"; 

    // URI templates used to get resources.
    
    public static final String BaseUri = "https://content.api.bingads.microsoft.com/shopping/v9.1";
    public static final String BmcUri = BaseUri + "/bmc/%d";
    public static final String BatchUri = BmcUri + "/products/batch";

    // Query strings. 
    
    public static final String queryString = "?alt=json";

    // Replace with your IDs.
    
    public static long merchantId = <merchantidgoeshere>; 
    public static long catalogId = <catalogidgoeshere>;

    // Will contain the list of IDs of products that we added, so we can 
    // remove them when we're done.
    
    public static List<String> ProductList = new ArrayList<String>();
    public static int batchOperationId = 0;

    // The maximum number of products that you can specify in a request
    // depends upon the number of product attributes that you specify, and
    // whether you compress the data. If you do not compress the data, 
    // you can specify about 2,000 products, and if you compress the 
    // data, you can specify about 12,000 products.

    // This example limits the list to 5 products.

    public final static int MAX_ENTRIES = 5;

	// This example shows how to use the batch feature to perform 
    // multiple operations (insert/update, get, and delete) in one
    // request.
    
	public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {

		try {
			Map<String, String> headers = getCredentialHeaders();

            String url = String.format(BatchUri + queryString, merchantId);
            
            // A batch operation may include any operation (insert, get, delete);
            // however, the operations may not act on the same product. This
            // example performs the batch operations separately.

            batchInsert(url, headers);
            batchGet(url, headers);
            batchDelete(url, headers);
		}
		catch (CapiException e) {
			if (e.getErrors() != null){
	    		printErrors(e.getErrors());
			}
			else
			{
				System.out.println(e.getMessage());
			}
		}
    	catch (IOException e) {
    		System.out.println(e.getMessage());
    	}
		catch (Exception e) {
			System.out.println("\n" + e.getMessage());
		}

	}

	// Get tokens for the authentication headers.

    private static Map<String, String> getCredentialHeaders()
    {
        // TODO: Add logic to get the user's access token.

        Map<String, String> headers = new HashMap<String, String>();
        headers.put("AuthenticationToken", accessToken);
        headers.put("DeveloperToken", devToken);

        return headers;
    }

    // POSTs the collection of batch operations to the specified URL.
    // This example compresses the data sent in the request. You must
    // use GZIP compression.
    
    private static BatchCollection batchRequest(String uri, Map<String, String> headers, BatchCollection batch) throws IOException, CapiException 
    {
    	HttpURLConnection connection = null;
    	URL url;
    	InputStream istream = null;
    	OutputStream ostream = null;
    	GZIPOutputStream zipOStream = null;
    	GZIPInputStream zipIStream = null;
    	BufferedReader reader = null;
    	BatchCollection batchOut = null;

    	try {
        	String json = jsonMapper.writeValueAsString(batch);

    		url = new URL(uri);
    		connection = (HttpURLConnection) url.openConnection();

    		connection.setRequestMethod("POST");
    		connection.setRequestProperty("Content-Type", "application/json");
    		connection.setRequestProperty("Content-Encoding", "gzip");
    		connection.setRequestProperty("Accept-Encoding", "gzip");

    		for (Entry\<String, String> header : headers.entrySet())
    		{
    			connection.setRequestProperty(header.getKey(), header.getValue());
    		}

    		connection.setDoInput(true);
    		connection.setDoOutput(true);
    		connection.setUseCaches(false);

    		ostream = connection.getOutputStream();
    		zipOStream = new GZIPOutputStream(ostream);
    		zipOStream.write(json.getBytes(StandardCharsets.UTF_8));
    		zipOStream.finish();
    		
    		int statusCode = connection.getResponseCode();

    		StringBuffer response = new StringBuffer();

    		if (statusCode >= HttpURLConnection.HTTP_BAD_REQUEST) {
    			istream = connection.getErrorStream();
    			
        		reader = new BufferedReader(new InputStreamReader(istream));
        		char[] buffer = new char[2048];
        		int len = 0;
        		
        		while ((len = reader.read(buffer)) != -1) {
        			response.append(new String(buffer, 0, len));
        		}
        		
    			if (statusCode == HttpURLConnection.HTTP_BAD_REQUEST) {
    	    		ContentError errors = jsonMapper.readValue(response.toString(), ContentError.class);
    				throw new CapiException("Batch request failed.\n", errors.getError().getErrors());
    			}
    			else {
    				throw new CapiException(response.toString());
    			}
    		}

    		istream = connection.getInputStream();
        		
    		response = new StringBuffer();
    		zipIStream = new GZIPInputStream(istream);
    		byte[] buffer = new byte[2048];
    		int len = 0;
    		
    		while ((len = zipIStream.read(buffer)) != -1) {
    			response.append(new String(buffer, 0, len));
    		}

    		zipIStream.close();
    		
    		System.out.println(response);

    		batchOut = jsonMapper.readValue(response.toString(), BatchCollection.class);
    	}
    	finally {
    		if (connection != null) {
    			connection.disconnect();
    		}

    		if (reader != null) {
    			reader.close();
    		}

    		if (istream != null) {
    			istream.close();
    		}
    		if (zipOStream != null) {
    			zipOStream.close();
    		}
    		
    		if (zipIStream != null) {
    			zipIStream.close();
    		}
    	}

    	return batchOut;
    }


    // Add 5 products to the catalog. Save the IDs to use in BatchGet
    // and BatchDelete.

    private static void batchInsert(String url, Map<String, String> headers) throws IOException, CapiException
    {
    	BatchCollection batchCollection = new BatchCollection();
    	batchCollection.setEntries(new ArrayList<BatchEntry>());

        for (int i = 1; i < MAX_ENTRIES; i++)
        {
            Product product = getProduct();
            product.setOfferId(String.valueOf(i));
            BatchEntry batchEntry = new BatchEntry(i, merchantId, "insert", null, product);
            batchCollection.getEntries().add(batchEntry);
        }

        // If you try to insert too many products, the call fails with status code 413.

        BatchCollection batchOut = batchRequest(url, headers, batchCollection);

        for (int i = 0; i < batchOut.getEntries().size(); i++)
        {
            if (((BatchEntry) batchOut.getEntries().toArray()[i]).getErrors() != null)
            {
                System.out.printf("Failed to insert product %s.\n", 
                    ((BatchEntry) batchCollection.getEntries().toArray()[(int)(((BatchEntry) batchOut.getEntries().toArray()[i]).getBatchId())]).getProduct().getOfferId());
                printErrors(((BatchEntry) batchOut.getEntries().toArray()[i]).getErrors().getErrors());
            }
            else
            {
                String id = ((BatchEntry) batchOut.getEntries().toArray()[i]).getProduct().getId();
                ProductList.add(id);
                System.out.println("Inserted product " + id);
            }
        }
    }


    // Use GET operation on the products inserted by BatchInsert.

    private static void batchGet(String url, Map<String, String> headers) throws IOException, CapiException
    {
    	BatchCollection batchCollection = new BatchCollection();
    	batchCollection.setEntries(new ArrayList<BatchEntry>());
        batchOperationId = 1;

        for (String productId : ProductList)
        {
        	BatchEntry batchEntry = new BatchEntry(batchOperationId++, merchantId, "get", productId, null);
            batchCollection.getEntries().add(batchEntry);
        }

        BatchCollection batchOut = batchRequest(url, headers, batchCollection);

        for (int i = 0; i < batchOut.getEntries().size(); i++)
        {
            if (((BatchEntry) batchOut.getEntries().toArray()[i]).getErrors() != null)
            {
                System.out.printf("Failed to get product {0}.", 
                    ((BatchEntry) batchCollection.getEntries().toArray()[(int)(((BatchEntry) batchOut.getEntries().toArray()[i]).getBatchId())]).getProductId());
                printErrors(((BatchEntry) batchOut.getEntries().toArray()[i]).getErrors().getErrors());
            }
            else
            {
                if (((BatchEntry) batchOut.getEntries().toArray()[i]).getProduct() == null)
                {
                    System.out.printf("Product {0} was not found.",
                        ((BatchEntry) batchCollection.getEntries().toArray()[(int)(((BatchEntry) batchOut.getEntries().toArray()[i]).getBatchId())]).getProductId());
                }
                else
                {
                    printProduct(((BatchEntry) batchOut.getEntries().toArray()[i]).getProduct());
                    System.out.println();
                }
            }
        }
    }


    // Use DELETE operation on the products inserted by BatchInsert.

    private static void batchDelete(String url, Map<String, String> headers) throws IOException, CapiException
    {
    	BatchCollection batchCollection = new BatchCollection();
    	batchCollection.setEntries(new ArrayList<BatchEntry>());
        batchOperationId = 1;

        for (String productId : ProductList)
        {
        	BatchEntry batchEntry = new BatchEntry(batchOperationId++, merchantId, "delete", productId, null);
            batchCollection.getEntries().add(batchEntry);
        }

        BatchCollection batchOut = batchRequest(url, headers, batchCollection);

        for (int i = 0; i < batchOut.getEntries().size(); i++)
        {
            if (((BatchEntry) batchOut.getEntries().toArray()[i]).getErrors() != null)
            {
                System.out.printf("Failed to delete product %s.\n", 
                    ((BatchEntry) batchCollection.getEntries().toArray()[(int)(((BatchEntry) batchOut.getEntries().toArray()[i]).getBatchId())]).getProductId());
                printErrors(((BatchEntry) batchOut.getEntries().toArray()[i]).getErrors().getErrors());
            }
            else
            {
                System.out.println("Deleted product " + ((BatchEntry) batchCollection.getEntries().toArray()[i]).getProductId());
            }
        }
    }



    // This example creates a single product that it adds to the store.
    // The calling code overwrites offerId to ensure a new product is added.

    private static Product getProduct()
    {
        Product product = new Product();

        // The following properties are required.

        product.setAvailability(Availability.get(ProductAvailability.InStock));
        product.setChannel(ProductChannel.Online.toString());
        product.setCondition(ProductCondition.New.toString());
        product.setContentLanguage(ContentLanguage.EN.toString());
        product.setLink("http://www.contoso.com");
        product.setImageLink("https://tse3.mm.bing.net/th?id=Ma8674a23cc755995efecf822b3836f07o0&pid=Api");
        product.setOfferId("1");
        product.setPrice(new Price());
        product.getPrice().setCurrency("USD");
        product.getPrice().setValue((Double)1205.00);
        product.setTargetCountry(TargetCountry.US.toString());
        product.setTitle("Mens T-shirt");

        // If the manufacturer provides identifiers for brand, gtin
        // or mpn, you must set those fields, and set identifierExists
        // to true.
                
        product.setIdentifierExists(false);

        // The following properties are optional.

        OffsetDateTime time = OffsetDateTime.now().plusDays(45);
        product.setExpirationDate(time.format(DateTimeFormatter.ISO_INSTANT)); // "2017-06-03T20:47:27.0437485Z"

        return product;
    }


    // Print some of the product's fields.
    
    private static void printProduct(Product product)
    {
        System.out.println("product ID: " + product.getId());
        System.out.println("channel : " + product.getChannel());
        System.out.println("language: " + product.getContentLanguage());
        System.out.println("country: " + product.getTargetCountry());
        System.out.println("offer ID: " + product.getOfferId());
        System.out.println("expiry date: " + product.getExpirationDate());

        System.out.println("title: " + product.getTitle());

        if (product.getPrice() != null)
        {
        	System.out.printf("price: %4.2f %s\n", product.getPrice().getValue(), product.getPrice().getCurrency());
        }
    }

    
    // Print errors.
    
    private static void printErrors(List<Error> errors)
    {
        for (Error error : errors)
        {
            System.out.printf("reason: %s\nmessage: %s\ndomain: %s\n\n",
                error.getReason(), error.getMessage(), error.getDomain());
        }
    }
}

// Converting True/False to Boolean because the serializer can't.

class StringBooleanDeserializer extends JsonDeserializer<Boolean> {
	
	public Boolean deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
		return !"False".equals(parser.getText());
	}
}

@SuppressWarnings("serial")
class CapiException extends Exception {

	private List<Error> errors = null;
	
	public CapiException(String message) {
		super(message);
	}

	public CapiException(String message, List<Error> errors) {
		super(message);
		this.errors = errors;
	}

	public CapiException(Throwable cause) {
		super(cause);
	}

	public CapiException(String message, Throwable cause) {
		super(message, cause);
	}
	
	public List<Error> getErrors() { return this.errors; }
	public void setErrors(List<Error> value) { this.errors = value; }
}

// Defines the batch classes.

@JsonInclude(Include.NON_DEFAULT)
class BatchCollection
{
	private List<BatchEntry> entries;
	private String kind;

	public String getKind() { return this.kind; }  // content#productsCustomBatchResponse
	public void setKind(String value) { this.kind = value; }  
	
    public List<BatchEntry> getEntries() { return this.entries; }
    public void setEntries(List<BatchEntry> value) { this.entries = value; }
}

@JsonInclude(Include.NON_DEFAULT)
class BatchEntry
{
	private long batchId;
	private long merchantId;
	private String method;
	private String productId;
	private Product product;
	private BatchItemError errors;
	
    public BatchEntry() { }

    public BatchEntry(int batchId, long merchantId, String method, String productId, Product product)
    {
        this.batchId = batchId;
        this.merchantId = merchantId;
        this.method = method;
        this.productId = productId;
        this.product = product;
    }

    public long getBatchId() { return this.batchId; }
    public void setBatchId(long value) { this.batchId = value; }

    public long getMerchantId() { return this.merchantId; }
    public void setMerchantId(long value) { this.merchantId = value; }

    public String getMethod() { return this.method; }
    public void setMethod(String value) { this.method = value; }

    public String getProductId() { return this.productId; }
    public void setProductId(String value) { this.productId = value; }

    public Product getProduct() { return this.product; }
    public void setProduct(Product value) { this.product = value; }

    public BatchItemError getErrors() { return this.errors; }
}


class BatchItemError
{
	private List<Error> errors;
	
    public List<Error> getErrors() { return this.errors; }
}


// Defines a product resource.

@JsonInclude(Include.NON_DEFAULT)
class Product
{
	private List<String> additionalImageLinks;
	
    @JsonProperty("adult")
    @JsonDeserialize(using=StringBooleanDeserializer.class)
	private Boolean isAdult;
    private String adwordsGrouping;
    private List<String> adwordsLabels;
    private String adwordsRedirect;
    private String ageGroup;
    private String availability;
    private String availabilityDate;
    private String brand;
    private String channel;
    private String color;
    private String condition;
    private String contentLanguage;
    private List<ProductCustomAttribute> customAttributes;
    private List<ProductCustomGroup> customGroups;
    private String customLabel0;
    private String customLabel1;
    private String customLabel2;
    private String customLabel3;
    private String customLabel4;
    private String description;
    private List<ProductDestination> destinations;
    private String energyEfficiencyClass;
    private String expirationDate;
    private String gender;
    private String googleProductCategory;
    private String gtin;
    private String id;

    @JsonDeserialize(using=StringBooleanDeserializer.class)
    private Boolean identifierExists;
    private String imageLink;

    @JsonDeserialize(using=StringBooleanDeserializer.class)
    private Boolean isBundle;
    private String itemGroupId;
    private String kind;  //For example: "kind":"content#product"
    private String link;
    private String material;
    
    @JsonProperty("multipack")
    private Long multipackQuantity;
    private String mobileLink;
    private String mpn;
    private String offerId;

    @JsonDeserialize(using=StringBooleanDeserializer.class)
    private Boolean onlineOnly;
    private String pattern;
    private Price price;
    private String productType;
    private Price salePrice;
    private String salePriceEffectiveDate;
    private List<ProductShipping> shipping;
    private ProductShippingWeight shippingWeight;
    private String shippingLabel;
    private List<String> sizes;
    private String sizeSystem;
    private String sizeType;
    private String targetCountry;
    private List<ProductTax> taxes;
    private String title;
    private UnitPricing unitPricingBaseMeasure;
    private UnitPricing unitPricingMeasure;
    private List<String> validatedDestinations;
    
    

	public List<String> getAdditionalImageLinks() { return this.additionalImageLinks; }
	public void setAdditionalImageLinks(List<String> value) { this.additionalImageLinks = value; }

    public Boolean getIsAdult() { return this.isAdult; }
    public void setIsAdult(Boolean value) { this.isAdult = value; }

    public String getAdwordsGrouping() { return this.adwordsGrouping; }
    public void setAdwordsGrouping(String value) { this.adwordsGrouping = value; }

    public List<String> getAdwordsLabels() { return this.adwordsLabels; }
    public void setAdwordsLabels(List<String> value) { this.adwordsLabels = value; }

    public String getAdwordsRedirect() { return this.adwordsRedirect; }
    public void setAdwordsRedirect(String value) { this.adwordsRedirect = value; }

    public String getAgeGroup() { return this.ageGroup; }
    public void setAgeGroup(String value) { this.ageGroup = value; }

    public String getAvailability() { return this.availability; }
    public void setAvailability(String value) { this.availability = value; }

    public String getAvailabilityDate() { return this.availabilityDate; }
    public void setAvailabilityDate(String value) { this.availabilityDate = value; }

    public String getBrand() { return this.brand; }
    public void setBrand(String value) { this.brand = value; }

    public String getChannel() { return this.channel; }
    public void setChannel(String value) { this.channel = value; }

    public String getColor() { return this.color; }
    public void setColor(String value) { this.color = value; }

    public String getCondition() { return this.condition; }
    public void setCondition(String value) { this.condition = value; }

    public String getContentLanguage() { return this.contentLanguage; }
    public void setContentLanguage(String value) { this.contentLanguage = value; }

    public List<ProductCustomAttribute> getCustomAttributes() { return this.customAttributes; }
    public void setCustomAttributes(List<ProductCustomAttribute> value) { this.customAttributes = value; }

    public List<ProductCustomGroup> getCustomGroups() { return this.customGroups; }
    public void setCustomGroups(List<ProductCustomGroup> value) { this.customGroups = value; }

    public String getCustomLabel0() { return this.customLabel0; }
    public void setCustomLabel0(String value) { this.customLabel0 = value; }

    public String getCustomLabel1() { return this.customLabel1; }
    public void setCustomLabel1(String value) { this.customLabel1 = value; }

    public String getCustomLabel2() { return this.customLabel2; }
    public void setCustomLabel2(String value) { this.customLabel2 = value; }

    public String getCustomLabel3() { return this.customLabel3; }
    public void setCustomLabel3(String value) { this.customLabel3 = value; }

    public String getCustomLabel4() { return this.customLabel4; }
    public void setCustomLabel4(String value) { this.customLabel4 = value; }

    public String getDescription() { return this.description; }
    public void setDescription(String value) { this.description = value; }

    public List<ProductDestination> getDestinations() { return this.destinations; }
    public void setDestinations(List<ProductDestination> value) { this.destinations = value; }

    public String getEnergyEfficiencyClass() { return this.energyEfficiencyClass; }
    public void setEnergyEfficiencyClass(String value) { this.energyEfficiencyClass = value; }

    public String getExpirationDate() { return this.expirationDate; }
    public void setExpirationDate(String value) { this.expirationDate = value; }

    public String getGender() { return this.gender; }
    public void setGender(String value) { this.gender = value; }

    public String getGoogleProductCategory() { return this.googleProductCategory; }
    public void setGoogleProductCategory(String value) { this.googleProductCategory = value; }

    public String getGtin() { return this.gtin; }
    public void setGtin(String value) { this.gtin = value; }

    public String getId() { return this.id; }
    public void setId(String value) { this.id = value; }

    public Boolean getIdentifierExists() { return this.identifierExists; }
    public void setIdentifierExists(Boolean value) { this.identifierExists = value; }

    public String getImageLink() { return this.imageLink; }
    public void setImageLink(String value) { this.imageLink = value; }

    public Boolean getIsBundle() { return this.isBundle; }
    public void setIsBundle(Boolean value) { this.isBundle = value; }

    public String getItemGroupId() { return this.itemGroupId; }
    public void setItemGroupId(String value) { this.itemGroupId = value; }

    public String getKind() { return this.kind; } 
    public void setKind(String value) { this.kind = value; } 

    public String getLink() { return this.link; }
    public void setLink(String value) { this.link = value; }

    public String getMaterial() { return this.material; }
    public void setMaterial(String value) { this.material = value; }

    public Long getMultipackQuantity() { return this.multipackQuantity; }
    public void setMultipackQuantity(Long value) { this.multipackQuantity = value; }

    public String getMobileLink() { return this.mobileLink; }
    public void setMobileLink(String value) { this.mobileLink = value; }

    public String getMpn() { return this.mpn; }
    public void setMpn(String value) { this.mpn = value; }

    public String getOfferId() { return this.offerId; }
    public void setOfferId(String value) { this.offerId = value; }

    public Boolean getOnlineOnly() { return this.onlineOnly; }
    public void setOnlineOnly(Boolean value) { this.onlineOnly = value; }

    public String getPattern() { return this.pattern; }
    public void setPattern(String value) { this.pattern = value; }

    public Price getPrice() { return this.price; }
    public void setPrice(Price value) { this.price = value; }

    public String getProductType() { return this.productType; }
    public void setProductType(String value) { this.productType = value; }

    public Price getSalePrice() { return this.salePrice; }
    public void setSalePrice(Price value) { this.salePrice = value; }

    public String getSalePriceEffectiveDate() { return this.salePriceEffectiveDate; }
    public void setSalePriceEffectiveDate(String value) { this.salePriceEffectiveDate = value; }

    public List<ProductShipping> getShipping() { return this.shipping; }
    public void setShipping(List<ProductShipping> value) { this.shipping = value; }

    public ProductShippingWeight getShippingWeight() { return this.shippingWeight; }
    public void setShippingWeight(ProductShippingWeight value) { this.shippingWeight = value; }

    public String getShippingLabel() { return this.shippingLabel; }
    public void setShippingLabel(String value) { this.shippingLabel = value; }

    public List<String> getSizes() { return this.sizes; }
    public void setSizes(List<String> value) { this.sizes = value; }

    public String getSizeSystem() { return this.sizeSystem; }
    public void setSizeSystem(String value) { this.sizeSystem = value; }

    public String getSizeType() { return this.sizeType; }
    public void setSizeType(String value) { this.sizeType = value; }

    public String getTargetCountry() { return this.targetCountry; }
    public void setTargetCountry(String value) { this.targetCountry = value; }

    public List<ProductTax> getTaxes() { return this.taxes; }
    public void setTaxes(List<ProductTax> value) { this.taxes = value; }

    public String getTitle() { return this.title; }
    public void setTitle(String value) { this.title = value; }

    public UnitPricing getUnitPricingBaseMeasure() { return this.unitPricingBaseMeasure; }
    public void setUnitPricingBaseMeasure(UnitPricing value) { this.unitPricingBaseMeasure = value; }

    public UnitPricing getUnitPricingMeasure() { return this.unitPricingMeasure; }
    public void setUnitPricingMeasure(UnitPricing value) { this.unitPricingMeasure = value; }

    public List<String> getValidatedDestinations() { return this.validatedDestinations; }
    public void setValidatedDestinations(List<String> value) { this.validatedDestinations = value; }
}


// Defines a product's price.

class Price
{
	private String currency;
	private Double value;
	
    public String getCurrency() { return this.currency; }
    public void setCurrency(String value) { this.currency = value; }

    public Double getValue() { return this.value; }
    public void setValue(Double value) { this.value = value; }
}


// Defines a product's tax.

@JsonInclude(Include.NON_DEFAULT)
class ProductTax
{
	private String country;
	private Long locationId;
	private String postalCode;
	private Double rate;
	private String region;

	@JsonDeserialize(using=StringBooleanDeserializer.class)
	private Boolean taxShip;

    public String getCountry() { return this.country; }
    public void setCountry(String value) { this.country = value; }

    public Long getLocationId() { return this.locationId; }
    public void setLocationId(Long value) { this.locationId = value; }

    public String getPostalCode() { return this.postalCode; }
    public void setPostalCode(String value) { this.postalCode = value; }

    public Double getRate() { return this.rate; }
    public void setRate(Double value) { this.rate = value; }

    public String getRegion() { return this.region; }
    public void setRegion(String value) { this.region = value; }

    public Boolean getTaxShip() { return this.taxShip; }
    public void setTaxShip(Boolean value) { this.taxShip = value; }
}



// Defines a product's shipping weight.

class ProductShippingWeight
{
	private String unit;
	private Double value;
	
    public String getUnit() { return this.unit; }
    public void setUnit(String value) { this.unit = value; }

    public Double getValue() { return this.value; }
    public void setValue(Double value) { this.value = value; }
}


// Defines a product's shipping cost.

@JsonInclude(Include.NON_DEFAULT)
class ProductShipping
{
	private String country;
	private String locationGroupName;
	private Long locationId;
	private String postalCode;
	private Price price;
	private String region;
	private String service;
	
    public String getCountry() { return this.country; }
    public void setCountry(String value) { this.country = value; }

    public String getLocationGroupName() { return this.locationGroupName; }
    public void setLocationGroupName(String value) { this.locationGroupName = value; }

    public Long getLocationId() { return this.locationId; }
    public void setLocationId(Long value) { this.locationId = value; }

    public String getPostalCode() { return this.postalCode; }
    public void setPostalCode(String value) { this.postalCode = value; }

    public Price getPrice() { return this.price; }
    public void setPrice(Price value) { this.price = value; }

    public String getRegion() { return this.region; }
    public void setRegion(String value) { this.region = value; }

    public String getService() { return this.service; }
    public void setService(String value) { this.service = value; }
}


// Defines per unit pricing.

class UnitPricing
{
	private String unit;
	private Double value;
	
    public String getUnit() { return this.unit; }
    public void setUnit(String value) { this.unit = value; }

    public Double getValue() { return this.value; }
    public void setValue(Double value) { this.value = value; }
}


// Defines a destination.

@JsonInclude(Include.NON_DEFAULT)
class ProductDestination
{
	private String destinationName;
	private String intention;
	
    public String getDestinationName() { return this.destinationName; }
    public void setDestinationName(String value) { this.destinationName = value; }

    public String getIntention() { return this.intention; }
    public void setIntention(String value) { this.intention = value; }
}


// Defines a product's custom attribute.

class ProductCustomAttribute
{
	private String name;
	private String type;
	private String unit;
	private String value;
	
    public String getName() { return this.name; }
    public void setName(String value) { this.name = value; }

    public String getType() { return this.type; }
    public void setType(String value) { this.type = value; }

    public String getUnit() { return this.unit; }
    public void setUnit(String value) { this.unit = value; }

    public String getValue() { return this.value; }
    public void setValue(String value) { this.value = value; }
}


// Defines a custom group of attributes.

class ProductCustomGroup
{
    @JsonProperty("customGroups")
	private List<ProductCustomAttribute> attributes;
    private String name;
	
    public List<ProductCustomAttribute> getAttributes() { return this.attributes; }
    public void setAttributes(List<ProductCustomAttribute> value) { this.attributes = value; }

    public String getName() { return this.name; }
    public void setName(String value) { this.name = value; }
}


// Defines the possible distribution channels.

enum ProductChannel
{
    Online,
    Local
}

//Defines the possible product conditions.

enum ProductCondition
{
	New,
	Refurbished,
	Used
}

//Defines the possible product availability.

enum ProductAvailability
{
	InStock,
	OutOfStock,
	PreOrder
}

//Defines the possible content languages.

enum ContentLanguage
{
	EN,
	DE,
	FR
}

//Defines the possible target countries or regions.

enum TargetCountry
{
	AU,
	DE,
	FR,
	GB,
	US
}

//Defines the possible genders.

enum Gender
{
	Female,
	Male,
	Unisex
}


// Classes used to handle errors.

class Error
{
	private String location;
	private String locationType;
	private String domain;
	private String message;
	private String reason;
	
    public String getLocation() { return this.location; }

    public String getLocationType() { return this.locationType; }

    public String getDomain() { return this.domain; }

    public String getMessage() { return this.message; }

    public String getReason() { return this.reason; }
}

class ErrorCollection
{
	private List<Error> errors;
	
    public List<Error> getErrors() { return this.errors; }
}


class ContentError
{
	private ErrorCollection error;
	
    public ErrorCollection getError() { return this.error; }
}

# A simple example that shows how to batch multiple inserts/updates 
# in a single request. This example adds products to the default
# catalog.

import string
import json
import requests
from datetime import datetime, timedelta

# Creates the endpoint to send the batch request to. If you want to
# add products to a specific catalog, include the following line:
# BATCH_URI_WITH_CATALOG = BATCH_URI + '?bmc-catalog-id={1}

BASE_URI = 'https://content.api.bingads.microsoft.com/shopping/v9.1'
BMC_URI = BASE_URI + '/bmc/{0}'  # {0} will contain the merchant ID 
BATCH_URI = BMC_URI + '/products/batch'

DEV_TOKEN = '<developer token goes here>'
MERCHANT_ID = '<merchant store id goes here>'

# This example does not show how to get the access token. Copy
# it here from another source.

AUTHENTICATION_TOKEN = "<oauth access token goes here>"

AUTHENTICATION_HEADERS = {'DeveloperToken': DEV_TOKEN, 'AuthenticationToken': AUTHENTICATION_TOKEN}


# Prints the batch error.

def print_error(errors):
    print('HTTP status code: ' + errors['code'])
    print('The following issues were found:\n')

    for error in errors['errors']:
        print('Reason: {0}'.format(error['reason']))
        print('Message: {0}'.format(error['message']))
        print()


# Prints warning messages if there's an issue with a product.
# For example, the API returns a warning if you don't specify the 
# brand, gtin, and mpn identifiers and forgot to set the 
# identifierExists field to false.

def print_warnings(warnings):
    print('The following issues were found:\n')

    for warning in warnings:
        print('Reason: {0}'.format(warning['reason']))
        print('Message: {0}'.format(warning['message']))
        print()


# Check batch response for errors and warnings.
# Either the 'product' or 'errors' field will exist, not both.
# If the 'product' field exists, print the product's ID.
# Otherwise, print the errors that occurred.

def check_for_errors(batch):
    for item in batch['entries']:
        print('Batch ID:' + item['batchId'])

        try:
            print('Successfully added product: ' + item['product']['id'])

            if 'warnings' in item['product']:
                print_warnings(item['product']['warnings']) 

        except KeyError as ex:
            print_error(item['errors'])


# Runs the batch request.

def run_batch_request(batch):
    url = BATCH_URI.format(MERCHANT_ID)
    response = requests.post(url, headers=AUTHENTICATION_HEADERS, data=json.dumps(batch))
    response.raise_for_status()
    print('Request activity ID: {0}'.format(response.headers['WebRequestActivityId']))
    return json.loads(response.text)


# For simplicity, this example hard-codes a couple of records.

def get_batch_data():
    batch = {}
    batch['entries'] = []

    # Add the first batch record.

    product = {}
    product['offerId'] = 'xyz-abc-123'
    product['channel'] = 'Online'
    product['contentLanguage'] = 'en'
    product['targetCountry'] = 'US'
    product['title'] = 'product title 1'
    product['availability'] = 'in stock'
    product['condition'] = 'new'
    product['price'] = {}
    product['price']['currency'] = 'USD'
    product['price']['value'] = 50.50
    product['link'] = 'http://www.contoso.com/apperal/men/tshirts.htm'
    product['imageLink'] = 'http://www.contoso.com/pics/tees.jpg'
    product['promotionId'] = 'promo1!,promo2,promo3'
    # product['identifierExists'] = False  # Un-comment to remove warning case

    batchitem = {
        'batchId': 1, 
        'merchantId': MERCHANT_ID, 
        'method': 'Insert', 
        'product': product 
    }

    batch['entries'].append(batchitem)
    
    # Add the second batch record.

    product = {}
    product['offerId'] = 'xyz-def-456'
    product['channel'] = 'Online'
    product['contentLanguage'] = 'en'
    product['targetCountry'] = 'US'
    product['title'] = 'product title 2'
    product['availability'] = 'in stock'
    product['condition'] = 'new'
    product['price'] = {}
    product['price']['currency'] = 'USD'
    product['price']['value'] = 750.33
    product['link'] = 'http://www.contoso.com/apperal/men/shorts.htm'
    product['imageLink'] = 'http://www.contoso.com/pics/shorts.jpg'
    product['promotionId'] = 'promo1'
    product['identifierExists'] = False
    product['expirationDate'] = (datetime.utcnow()+timedelta(days=14)).strftime("%Y-%m-%dT%H:%M:%S")

    batchitem = {
        'batchId': 2, 
        'merchantId': MERCHANT_ID, 
        'method': 'Insert', 
        'product': product
    }

    batch['entries'].append(batchitem)

    return batch


def main():
    # The main entry point of this example

    try:

        batch = get_batch_data()
        batchResponse = run_batch_request(batch)
        check_for_errors(batchResponse)

    except Exception as ex:
        raise ex

# Main execution
if __name__ == '__main__':
    main()