4

I am using this library in C# to make REST calls to SalesForce: https://github.com/developerforce/Force.com-Toolkit-for-NET

I've run into a problem when trying Upsert into Product2. Here is the error message I am getting:

[{"errorCode":"METHOD_NOT_ALLOWED","message":"HTTP Method 'PATCH' not allowed. Allowed are GET,HEAD,POST"}]

The URL it generates:

https://na73.salesforce.com/services/data/v41.0/sobjects/Product2/ProductID__c/# H6 SHORT BARREL NUT

So clearly it wants me to not use upsert. What's weird though is I've been able to upsert on Accounts and Contacts as well as others. Even weirder is the methods it says are allowed to use on Product2 don't allow me to update existing products - I can only create new ones via POST. How do I update a product via the API?

Below is the code/payload I am working with just in case I'm doing something wrong...

public async Task<bool> UpsertProduct(SalesForceProduct product, string salesForceField, string salesForceFieldValue)
{
    bool Authenticated = await Authenticate();

    if (!Authenticated)
    {
        throw new Exception("[UpsertProduct] Failed to Authenticate");
    }

    Console.WriteLine("[UpsertProduct] Upserting a potentially new product");

    var upsertProductResponse = await forceClient.UpsertExternalAsync(SalesForceProduct.SObjectTypeName, salesForceField, salesForceFieldValue, product);
    if (!upsertProductResponse.Success)
    {
        Console.WriteLine($"[UpsertProduct] An error occurred upserting product {product.ProductID__c}: {upsertProductResponse.Errors.ToString()}");
        return false;
    }

    // The upserts for accounts work this way so I am guessing Product2 is - but I haven't made it this far yet...
    product.Id = upsertProductResponse.Id;

    if (product.Id == string.Empty)
    {
        Console.WriteLine($"[UpsertProduct] Updated existing product: {product.ProductID__c}");
    }
    else
    {
        Console.WriteLine($"[UpsertProduct] Created new product: {product.Id}");
    }

    return upsertProductResponse.Success;
}

And how I call the Upsert function:

var hProducts = sql.GetAllProducts();
var convertedProducts = Utilities.STORISProductToSalesForce(hProducts);

SalesForceProduct product = convertedProducts[0];                

await sfREST.Authenticate();
await sfREST.UpsertProduct(product, "ProductID__c", product.ProductID__c);

And the payload (product) I am trying to Upsert:

{"BrandID__c":"<No Value>","CaseSellPrice__c":0.0000,"Class__c":"","Color__c":"","DealCasePrice__c":0.0000,"DepthDimension__c":0.0,"Description2__c":"","GroupID__c":"<No Value>","KitOnly__c":"False","KitStatus__c":"N","Name":"<Unknown>","PieceSellPrice__c":0.0000,"ProductBenefits__c":"","ProductCode":"# H6 SHORT BARREL NUT","ProductID__c":"# H6 SHORT BARREL NUT","ProductTypeID__c":9999,"SpecialOrder__c":true,"Status__c":"","Style__c":"","SuggRetailPrice__c":0.0000,"VendorID__c":"<No Value>","WebAvailableOnWeb__c":"False","WebBenefits__c":""}
6

To use PATCH, you need to make sure you're encoding the ID. Assuming the URL in your question was generated literally, that's problematic. The URL should look like:

/services/data/v41.0/sobjects/Product2/ProductID__c/%23%20H6%20SHORT%20BARREL%20NUT

Characters other than A-Z, 0-9, #, and _ need to be URL-encoded. As it was, the URL was written in a way that the server couldn't "see" the ID, and assumed you were trying to do an insert, producing the error you received.


I feel like this might be a bug in the toolkit; it should automatically be encoding URL parameters for you, but it doesn't. You can encode it yourself by changing a line of code:

var upsertProductResponse = await forceClient.UpsertExternalAsync(SalesForceProduct.SObjectTypeName, salesForceField, Uri.EscapeUriString(salesForceFieldValue), product);

One final note: the External ID field must not be specified in your JSON payload. To do so is an error.


Demo


Incorrect:

enter image description here


Correct:

enter image description here

  • 1
    I can't believe I overlooked that. I was so focused on the METHOD_NOT_ALLOWED error message I didn't even notice it. I've added the encoding line directly to the library so going forward all calls will be encoded properly. Thank you so much! – duckwizzle Apr 11 '18 at 14:55
  • 1
    @duckwizzle That was my idea; I might fork the library and make some customizations to it. It looks useful, but it needs some work. – sfdcfox Apr 11 '18 at 14:59
  • @sfdcfpx Awesome man. If you do shoot me a link. I have about 2 weeks of Sales Force experience under by belt so if more stuff needs to change I probably won't notice it and would love to see what you improve on. Also, FYI if you make that change, Uri.EscapeUriString did not convert # to %23 - I did it manually after the encoding. I see a lot of product IDs with characters that don't auto encode. This should be fun – duckwizzle Apr 11 '18 at 15:09
  • @duckwizzle I'll play with it; it's possible that's the wrong method (I'm not super-familiar with C#, so it'll give me some experience tinkering with this). – sfdcfox Apr 11 '18 at 15:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.