:::: MENU ::::
Posts tagged with: salesforce

BULK remove of DatedConversionRate

I screw up DatedConversionRate data loading with ~2800 records created with incorrect StartDate. As I can retrieve it via SOQL I quickly created some apex script. Something like that

delete [
    SELECT Id
    FROM DatedConversionRate
    WHERE CreatedDate = TODAY
    AND StartDate < 2018-01-01
];

and got this

DML operation Delete not allowed on List<DatedConversionRate>

Not cool at all. Also I could not update StartDate with error

DML operation Update not allowed on List<DatedConversionRate>

Okay. Quick googling give me the solution. Here’s solution for create/update and delete DatedConversionRate via SOAP API. So, basically you can do it from Apex

HttpRequest req = new HttpRequest();
req.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm() + '/services/data/v45.0/sobjects/DatedConversionRate/04w0e0000000Po1AAE?_HttpMethod=DELETE');
req.setHeader('Authorization', 'OAuth ' + UserInfo.getSessionId());
req.setHeader('Content-Type', 'application/json');
req.setMethod('POST');
HttpResponse res = (new Http()).send(req);

But because I had ~2800 records to remove I decided to go with python

import request

record_ids_to_delete = ['04w0e0000000Po1AAE','04w0e0000000Po0AAE','04w0e0000000PnzAAE', ...2800 ids...]
headers = {"Authorization": "Bearer AQMAQPJ.ZAiHxG.LUaDqgUabvVdCao77Lq2R2kzcOP0fwMk7lbHA4G7va4U1jRfL_W0hFRBswLYTox2q1yLAsfL93MismKCC", "Content-Type": "application/json"}
for record_id in record_ids_to_delete:
    requests.post('https://my-org-name.my.salesforce.com/services/data/v45.0/sobjects/DatedConversionRate/' + record_id + '?_HttpMethod=DELETE', headers=headers)

this script might be much more improved with session id generation and using SOQL for retrieving record ids. But I hope that this is one time thing and copying/pasting session id from browser/any other source of session id as well as quick generation of monstrous array in sublime text are not so big deal when you have to fix it asap.


sun.security.validator.ValidatorException: PKIX path building failed in Salesforce

Working on integration Salesforce with external system and vice versa through both REST and SOAP APIs you might get the following exception

sun.security.validator.ValidatorException: 
PKIX path building failed: 
sun.security.provider.certpath.SunCertPathBuilderException: 
unable to find valid certification path to requested target

1. Salesforce to external system integration errors

The first thing to check is certificate on external system side. Most likely the problem is on this side.

1.1 The problem with certificate itself. Any issue with certificate on 3rd party side will cause the error. You can check this site for more details and examples on certificates issues.

Here’re a few possible reason why certificate is not trusted:

  1. certificate is expired
  2. certificate is for a wrong host
  3. certificate is self-signed
  4. certificate with untrusted root
  5. certificate is revoked
  6. certificate is invalid by key pinning

The only possible solution for these cases is to install correct certificate on a server.

1.2 When certificate on a server is valid and you can access the host by browser with no error it might be a sign of a broken certificate chain of trust.

Salesforce’s certificate trust policy is to require server and client certificate chains to include all intermediate certificates that exist between the server or client certificate and the chain’s root certificate

You can review salesforce trusted certificates via https://INSTANCE.salesforce.com/cacerts.jsp like ( https://slepenkov-dev-ed.my.salesforce.com/cacerts.jsp)

Most possibly your server has no intermediate, root CA or several certificates installed/added and thus these certificates are not added to server response during handshake.

On server you need to have all certificates in the chain. Different servers has different configuration and different locations for a certificates but in any case server must provide a whole chain to a client in order to be trusted. Structured in the following way

You can verify this case with the following command:

openssl s_client -connect techcrunch.com:443 

Valid result will be as follow:

When chain is broken the same command will produce the following result:

with final error messages

Verify return code: 21 (unable to verify the first certificate)

or

verify return code: 20 (unable to get local issuer certificate)

All you need to fix that is to add root CA certificate and/or intermediate certificates if needed in order to send server response with entire certificates chain during the handshake.

2. External System to Salesforce

When connecting to Salesforce from external application you might get the same error. This time error means that you server have no some salesforce chain of trust certificates. You can add it one by one or as a certificate bundle to your server.

Just download certificates chain with this command

openssl s_client -showcerts -connect slepenkov-dev-ed.my.salesforce.com:443

The output will contain all certificates in chain and you can copy and add it to your server certificate store.


Convert Rich text field images to Data URI format in VisualForce

A few weeks ago I had a task to convert Visualforce page into Google Document. I used Javascript to integrate Salesforce with Google Drive and the basic scenario was to render some Visualforce page. This page was a mix of some parent object with several rich text fields and many other fields with many child records linked to parent. These child records also has a few rich text fields and this page was totally ugly from UI perspective but extremely useful from business perspective.

When the page was completed and I tested that I can send a some part of HTML markup of VF page to Google Drive and Google transformed it to correct Google Document I was pretty excited. But. I faced an issue of sending salesforce’s rich text field content into Google drive document. When the plain text and formatting is sent with no issue, sending an images of rich text fields was not working at all.

You know that a rich text fields in Salesforce is just a HTML markup. HTML markup usually contains a references to resources outside of the page context and receives this content via network. When you add an image into rich text field from Salesforce UI you have 2 options:

  1. Upload file into Salesforce from your own computer
  2. Give SF a link to some web resource

Externally stored images are transferred fine into Google Drive. But images which stored inside Salesforce can’t be exported via link because it’s not publicly available. Actually rich text field images refer to https://*.content.force.com/servlet/rtaImage?.  So, if you want to send these images via network you have to transform it to Data URI format. My initial desire was to use JS on VisualForce as it described here: convert image to data uri with javascript. That’s a really simple and clean solution. But real pain point here is the fact that VF page and salesforce images are on different domains and you can’t use it without enabled CORS on image server side. It’s obvious that we have no access to manage headers which salesforce’s image server will return. So, JS solution was not applied here what was really upset.

The another option for such case is an apex server side processing. That approach is not so cool but at least pretty straightforward. The scenario is follow: get rich text field, parse it and find all references to images which stored in salesforce, convert it to Data URI and rebuild a rich text field with these images. Easy.


public static String convertRichTextImageLinkToBinary(String richText) {
    String imgTagPattern = '<img alt="User-added image" src="https://__sf_org_name__(.+?)content\\.force\\.com(.+?)>';
    List<String> dataUriImgs = new List<String>();
    Matcher imgMatcher = Pattern.compile(imgTagPattern).matcher(richText);

    while (imgMatcher.find()) {
        String imageTag = imgMatcher.group();
        String imageURL = imageTag.substringBetween(' src="', '"');
        String decodedURL = imageURL.unescapeHtml4();
        PageReference page = new PageReference(decodedURL);
        dataUriImgs.add('<img alt="User-added image" src="data:image/png;base64, ' + EncodingUtil.base64Encode(page.getContent()) + '">');
    }

    // can cause Regex too complex Exception on large rich text fields
    // for (Integer i = 0; i < dataUriImgs.size(); i++) {
    //     richText = richText.replaceFirst(imgTagPattern, dataUriImgs[i]);
    // }
    List<String> rTxs = richText.split('<img alt="User-added image" src="https://targetprocess(.+?)content\\.force\\.com(.+?)>');
    String result = '';
    for (Integer i = 0; i < dataUriImgs.size(); i++) {
        result += (rTxs[i] + dataUriImgs[i]);
    }
    result += rTxs[rTxs.size() - 1];
    return result; 
}
 

This code transforms passed rich text field images to Data URI format. You can use it from Visualforce controller for converting your rich text and exposing it on a page.

Note the following:

If you use getContent in a test method, the test method fails. getContent is treated as a callout in API version 34.0 and later.

That’s quote from official documentation.

 

 


Pages:1234