Tuesday, November 15, 2016

PDF to be generated in Batch classes through a REST webservice


Use Case
Piece of code that allows PDF to be generated in Batch classes through a REST webservice. SFDC currently does not allow usage of the getContent function to generate a PDF from a Visualforce page in a Batch context. The utility can generate either the Blob representation of the Visualforce PDF page or insert it as an Attachment to a given record and return the Attachment salesforce ID.

 Solution 
Created a REST webservice utility that can be called from a Batch class which returns the BLOB representation or an attachment ID from the Visualforce template provided.
Reusable Code

1    Utility Class

public class LetterPrintUtilities {
        
    private static final String STR_HTTP_METHOD_GET = 'GET';
    private static final String STR_HTTP_METHOD_POST = 'POST';
    private static final String STR_AUTHORIZATION = 'Authorization';
    private static final String STR_ACCEPT = 'Accept';
    private static final String STR_ACCEPT_VALUE = 'application/json';
    private static final String STR_CONTENT_TYPE = 'Content-Type';
    private static final String STR_CONTENT_TYPE_VALUE = 'application/json; charset=UTF-8';
    private static final String STR_OAUTH = 'OAuth';
    private static final String STR_TEST_PDF = 'This is a test BLOB Creation';
    
    private static Http http = new Http();
    private static HttpRequest req = new HttpRequest();
    private static HttpResponse res;
    
    private Blob pdfContent;
    private Id attachmentRec;
    
    
    private static final Map<String, PageReference> TEMPLATE_NAME_MAPPING = new Map<String, PageReference> {
        /** START: DO NOT REMOVE THIS ITEM, THIS IS USED FOR TEST CLASS **/
        'test_data_for_test_class' => new PageReference('/apex/test_data_for_test_class'),
        /**                        END                                **/
        
        'sample_renderaspdf' => Page.SAMPLE_RenderAsPdf
        
        //All keys in this map should be in lower case
    };
    
    public Blob getPdfContentBlob() {
        return this.pdfContent;
    }
    public Id getAttachmentId() {
        return this.attachmentRec;
    }
    
    public void retrieveLetter(PageReference vfPage, Map<String, Object> params) {
        this.pdfContent = null;
        PageReference pgToGet = vfPage;
        
        if(params != null) {
            for(String s : params.keySet()) {
                pgToGet.getParameters().put(s, String.valueOf(params.get(s)));
            }
        }
        
        if(!Test.isRunningTest()) {
            this.pdfContent = pgToget.getContentAsPDF();
        } else {
            this.pdfContent = Blob.toPDF(STR_TEST_PDF);
        }
    }
    
    public void retrieveLetter(PageReference vfPage, Map<String, Object> params, Id attachTo, String fileName) {
        retrieveLetter(vfPage, params);
        attachToRecord(attachTo, fileName);
    }
    
    public Id attachToRecord(Id recordToAttachTo, String fileName) {
        Database.SaveResult result;
        if(pdfContent != null) {
            Attachment attachment = new Attachment();
            attachment.Body = pdfContent;
            attachment.Name = fileName + '.pdf';
            attachment.ParentId = recordToAttachTo;
            
            try {
                result = Database.insert(attachment);
                this.attachmentRec = result.getId();
            } catch(DmlException dme) {
                throw new LetterPrintingException('BATCH_LETTER_EXCEPTION: Attachment insert failed (' + dme.getMessage() + ')');
            }
        } else {
            throw new LetterPrintingException('BATCH_LETTER_EXCEPTION: PDF is not generated');
        }
        return result.getId();
    }
    
    public static Boolean isTemplateAvailable(String templateName) {
        return TEMPLATE_NAME_MAPPING.containsKey(templateName);
    }
    public static PageReference getTemplate(String templateName) {
        return TEMPLATE_NAME_MAPPING.get(templateName);
    }
    
    
    private static void setupHttp() {
        String authorizationHeader = STR_OAUTH + ' ' + UserInfo.getSessionId();
        req.setMethod(STR_HTTP_METHOD_POST);
        req.setHeader(STR_AUTHORIZATION, authorizationHeader);
        req.setHeader(STR_ACCEPT, STR_ACCEPT_VALUE);
        req.setHeader(STR_CONTENT_TYPE, STR_CONTENT_TYPE_VALUE);
    }
    
    
    //base method to be called out
    public static Blob generateLetter(PageReference pg, Map<String, String> params) {
        
        setupHttp();
        req.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm() + '/services/apexrest/LetterPrintGetBlob/');
        
        String pageUrl = getTemplateFromPageRef(pg);
        if(isTemplateAvailable(pageUrl)) {
            req.setBody(new LetterRequestPost(pageUrl, params).getJsonValue());
            if(!Test.isRunningTest()) {
                res = http.send(req);
            } else {
                res = new HttpResponse();
                res.setBody(String.valueOf(Blob.toPDF(STR_TEST_PDF)));
            }
        }
        return Blob.valueOf(res.getBody());
    }
    
    public static Blob generateLetter(PageReference pg, Map<String, String> params, Id attachTo, String flName) {
        setupHttp();
        req.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm() + '/services/apexrest/LetterPrintGetAttachment/');
        
        String pageUrl = getTemplateFromPageRef(pg);
        if(isTemplateAvailable(pageUrl)) {
            req.setBody(new LetterRequestPostAttachTo(pageUrl, params, attachTo, flName).getJsonValue());
            if(!Test.isRunningTest()) {
                res = http.send(req);
            } else {
                res.setBody(String.valueOf(Blob.toPDF(STR_TEST_PDF)));
            }
        }
        return Blob.valueOf(res.getBody());
    }
    
    
    //Class being serialized as JSON to be sent to the REST webservice
    private class LetterRequestPost {
        String templateName;
        Map<String, String> params;
        LetterRequestPost(String templateName, Map<String, String> params) {
            this.templateName = templateName;
            this.params = params;
        }
        
        String getJsonValue() {
            String jsonVal = JSON.serializePretty(this);
            return jsonVal;
        }
    }
    
    private class LetterRequestPostAttachTo {
        String templateName;
        Map<String, String> params;
        String fileName;
        Id recordToAttachTo;
        LetterRequestPostAttachTo(String templateName, Map<String, String> params, Id attachTo, String flName) {
            this.templateName = templateName;
            this.params = params;
            this.recordToAttachTo = attachTo;
            this.fileName = flName;
        }
        
        String getJsonValue() {
            String jsonVal = JSON.serializePretty(this);
            return jsonVal;
        }
    }
    
    private static String getTemplateFromPageRef(PageReference pr){
        String baseStr = pr.getURL();
        if(baseStr != null) {
            while(baseStr.contains('/')) {
                baseStr = baseStr.substringAfter('/');
            }
            if(baseStr.contains('?')) {
                baseStr = baseStr.substringAfter('?');
            }
        }
        return baseStr;
    }
    class LetterPrintingException extends Exception {}
    
}



2)    Web service classes

@RestResource(urlMapping='/LetterPrintGetAttachment/*')
global class LetterPrintingAttachment {
    
    /**
    *    
    *    HTTP Post service that returns the Id of the inserted
    *    Attachment from the generated pdf
    *    
    **/
    @HttpPost
    global static String generateLetterAttachment(String templateName, Map<String, String> params, Id recordToAttachTo, String fileName) {
        LetterPrintUtilities util = new LetterPrintUtilities();
        if(LetterPrintUtilities.isTemplateAvailable(templateName)) {
            util.retrieveLetter(
                LetterPrintUtilities.getTemplate(templateName), 
                params,
                recordToAttachTo,
                fileName
            );
        }
        return util.getAttachmentId();
    }
    
}


@RestResource(urlMapping='/LetterPrintGetBlob/*')
global class LetterPrintingBlob {
    
    /**
    *    
    *    HTTP Post service that returns the Blob representation
    *    of the PDF VF Page which is deserialized in the Util 
    *    class
    *    
    **/
    @HttpPost
    global static String generateLetterBlob(String templateName, Map<String, String> params) {
        LetterPrintUtilities util = new LetterPrintUtilities();
        if(LetterPrintUtilities.isTemplateAvailable(templateName)) {
            util.retrieveLetter(
                LetterPrintUtilities.getTemplate(templateName), 
                params
            );
        }
        return EncodingUtil.base64Encode(util.getPdfContentBlob());
    }
    
}



Example:
Below is an example how the Above code can be used.
Batch Class:

Blob blobGenerated = LetterPrintUtilities.generateLetter(
Page.ExistingVfPage, //an existing VF page to be used as template 
new Map<String, String> { ‘Id’ => ‘00100000000AJHS’} //url parameters to be passed to the VF template
); 
Id attachmentId = LetterPrintUtilities.generateLetter(
Page.ExistingVfPage, //an existing VF page to be used as template 
new Map<String, String> { ‘Id’ => ‘00100000000AJHS’}, //url parameters to be passed to the VF template
‘00100000000AJHS’’,  //if an attachment is to be generated, the attachment will be attached to this record
‘PDF Order form’ //the attachment’s file name
); 



The method returns the BLOB content of the generated PDF or the ID of the attachment generated.

No comments:

Post a Comment