Nerd @ Work

When Salesforce is life!

[Salesforce / Apex] Queueable interfaces – Unleash the async power!

The next Winter ’15 release came with the new Queueable interface.

I wanted to go deep on this, and tried to apply its features to a real case.

If you are (like me) in a TLDR; state of mind, click here.

The main difference between future methods (remember the @future annotation? ) and queueable jobs are:

  • When you enqueue a new job you get a job id (that you can actually monitor)…you got it, like batch jobs or scheduled jobs!
  • You can enqueue a queueable job inside a queueable job (you cannot call a future method inside a future method!)
  • You can have complex Objects (such as SObjects or Apex Objects) in the job context (@future only supports primitive data types)

I wanted to show a pratical use case for this new feature.

Imagine you have a business flow in which you have to send a callout whenever a Case is closed.
Let’s assume the callout will be a REST POST method that accepts a json body with all the non-null Case fields that are filled exactly when the Case is closed (the endpoint of the service will be a simple RequestBin).

Using a future method we would pass the case ID to the job and so make a subsequent SOQL query: this is against the requirement to pass the fields we have in the case at the exact time of the update.
This may seem an exageration, but with big Orgs and hundreds of future methods in execution (due to system overload) future methods can be triggered after minutes and so the ticket state can be different from when the future was actually triggered.

For this implementation we will use a Callout__c Sobject with the following fields:

  • Case__c: master/detail on Case
  • Job_ID__c: external ID / unique / case sensitive, stores the job id
  • Send_on__c: date/time, when the callout has taken place
  • Duration__c: integer, milliseconds for the callout to be completed
  • Status__c: picklist, valued are Queued (default), OK (response 200), KO (response != 200) or Failed (exception)
  • Response__c: long text, stores the server response

Let’s start with the trigger:

    trigger CaseQueueableTrigger on Case (after insert, after update) {

    List calloutsScheduled = new List();
    for(Integer i = 0; i < Trigger.new.size(); i++){
        if((Trigger.isInsert || 
           Trigger.new[i].Status != Trigger.old[i].Status)
            && Trigger.new[i].Status == 'Closed' )
        {
            ID jobID = System.enqueueJob(new CaseQueuebleJob(Trigger.new[i]));
            calloutsScheduled.add(new Callout__c(Job_ID__c = jobID, 
                                                 Case__c = Trigger.new[i].Id,
                                                Status__c = 'Queued'));
        }
    }
    if(calloutsScheduled.size()>0){
        insert calloutsScheduled;
    }
}

The code iterates through the trigger cases and if they are created as “Closed” or the Status field changes to “Closed” a new job is enqueued and a Callout__c object is created.

This way we always have evidence on the system that the callout has been fired.

Let’s watch the job code

    public class CaseQueuebleJob implements Queueable, Database.AllowsCallouts {
    . . .
    }

The Database.AllowsCallouts allow to send a callout in the job.

Next thing is a simple constructor:

    /*
     * Case passed on class creation (the actual ticket from the Trigger)
     */
    private Case ticket{get;Set;}
    
    /*
     * Constructor
     */
    public CaseQueuebleJob(Case ticket){
        this.ticket = ticket;
    }

And this is the content of the interface’s execute method:

    
     // Interface method. 
     // Creates the map of non-null Case fields, gets the Callout__c object
     // depending on current context JobID.
     // In case of failure, the job is queued again.
     
    public void execute(QueueableContext context) {
        //1 - creates the callout payload
        String reqBody = JSON.serialize(createFromCase(this.ticket));
        
        //2 - gets the already created Callout__c object
        Callout__c currentCallout = [Select Id, Status__c, Sent_on__c, Response__c, Case__c,
                                     Job_ID__c From Callout__c Where Job_ID__c = :context.getJobId()];
        
        //3 - starting time (to get Duration__c)
        Long start = System.now().getTime();
        
        //4 - tries to make the REST call
        try{
            Http h = new Http();
            HttpRequest request = new HttpRequest();
            request.setMethod('POST');
            //change this to another bin @ http://requestb.in
            request.setEndpoint('http://requestb.in/nigam7ni');
            request.setTimeout(60000);
            request.setBody(reqBody);
            HttpResponse response = h.send(request);
            
            //4a - Response OK
            if(response.getStatusCode() == 200){
                currentCallout.status__c = 'OK';
            //4b - Reponse KO
            }else{
                currentCallout.status__c = 'KO';
            }
            //4c - saves the response body
            currentCallout.Response__c = response.getBody();
        }catch(Exception e){
            //5 - callout failed (e.g. timeout)
            currentCallout.status__c = 'Failed';
            currentCallout.Response__c = e.getStackTraceString().replace('n',' / ')+' - '+e.getMessage();
            
            //6 - it would have been cool to reschedule the job again :(
            /*
             * Apprently this cannot be done due to "Maximum callout depth has been reached." exception
            ID jobID = System.enqueueJob(new CaseQueuebleJob(this.ticket));
            Callout__c retry = new Callout__c(Job_ID__c = jobID, 
                                                 Case__c = this.ticket.Id,
                                                Status__c = 'Queued');
            insert retry;
            */
        }
        //7 - sets various info about the job
        currentCallout.Sent_on__c = System.now();
        currentCallout.Duration__c = system.now().getTime()-start;
        update currentCallout;
        
        //8 - created an Attachment with the request sent (it could be used to manually send it again with a bonification tool)
        Attachment att = new Attachment(Name = 'request.json', 
                                        Body = Blob.valueOf(reqBody), 
                                        ContentType='application/json',
                                       ParentId = currentCallout.Id);
        insert att;
    }

These are the steps:

  1. Creates the callout JSON payload to be sent (watch the method in the provided github repo) for more details (nothing more than a describe and a map)
  2. Gets the Callout__c object created by the trigger (and using the context’s Job ID)
  3. Gets the starting time of the callout being executed (to calculate the duration)
  4. Tries to make the rest call

    1. Server responded with a 200 OK
    2. Server responded with a non OK status (e.g. 400, 500)
    3. Saves the response body in the Response__c field
  5. Callout failed, so fills the Respose__c field with the stacktrace of the exception (believe me this is super usefull when trying to get what happened, expecially when you have other triggers / code in the OK branch of the code)
  6. Unfortunately if you try to enqueue another job after a callout is done you get the following error Maximum callout depth has been reached., this is apparently not documented, but it should be related by the fact that you can have only 2 jobs in the queue chain, so apparently if you queue the same job you get this error.
    This way the job would have tried to enqueue another equal job for future execution.
  7. Sets time fields on the Callout__c object
  8. Finally creates an Attachment object with the JSON request done: this way it can be expected, knowing the precise state of the Case object sent and can be re-submitted using a re-submission tool that uses the same code (Batch job?).

This is a simple Callout__c object on CRM:

And this is an example request:

{
    "values": {
        "lastmodifiedbyid": "005w0000003fj35AAA",
        "businesshoursid": "01mw00000009wh7AAA",
        "engineeringreqnumber": "767145",
        "casenumber": "00001001",
        "product": "GC1060",
        "planid": "a05w000000Gpig7AAB",
        "ownerid": "005w0000003fj35AAA",
        "createddate": "2014-08-09T09:54:17.000Z",
        "origin": "Phone",
        "isescalated": false,
        "status": "Closed",
        "slaviolation": "Yes",
        "accountid": "001w0000019wqEIAAY",
        "systemmodstamp": "2014-11-03T19:33:31.000Z",
        "isdeleted": false,
        "priority": "High",
        "id": "500w000000fqNRaAAM",
        "lastmodifieddate": "2014-11-03T19:33:31.000Z",
        "isclosedoncreate": true,
        "createdbyid": "005w0000003fj35AAA",
        "contactid": "003w000001EetwEAAR",
        "type": "Electrical",
        "closeddate": "2013-06-20T18:59:51.000Z",
        "subject": "Performance inadequate for second consecutive week",
        "reason": "Performance",
        "potentialliability": "Yes",
        "isclosed": true
    }
}

The code and the related metadata is available on this GitHub repo.

[Salesforce / Lightning] Loading scripts

a{ color: red !important;}

UPDATE

Due to the introduction of the ltng:require, this post is no more a valid solution. Refer to the official Lightning documentation.

This post has been more like a request for help, rather than a technical blog post, but it came to be an awesome way to see Salesforce community in action and ready to help!In the last Dreamforce 14 big Mark presented the Lightning framework for fast development of reusable components (see details here and the awesome Topcoder’s track).Click here for the “Lightning Components Developer’s Guide”, well written and clear.I’ve noticed a strange behavior regarding external javascript libraries loading.These are the requirements regarding external script loading:

  • You can only load external libraries got from a static resource
  • You cannot use the {!$Resource.resourceName} expression because we are not inside a VisualForce page, so you have to simply refer to “/resource/[resourceName]” in your <script> tags
  • From page 100: “If you want to use a library, such as jQuery, to access the DOM, use it in afterRender().

Apparently the last sentence is not true.The problem arose because I loaded jQuery + Bootstrap and sometimes (and randomly) the Bootstrap plugin did not load because of jQuery was not yet loaded: the cause was certanly the fact that libraries were not loaded sequentially!TLDR; click here for the solution!This is what I’m trying to do:

BlogScriptApp.app

<aura:application>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <aura:handler event="aura:doneRendering" action="{!c.doneRendering}"/>
    <script src="/resource/BlogScripts/jquery.min.js" ></script>    
    <div id="afterLoad">Old value</div>
</aura:application>

BlogScriptAppController.js

({
 doInit : function(component, event, helper) {
        try{
   $("#afterLoad").html("VALUE CHANGED!!!");
            console.log('doInit: Success');
        }catch(Ex){
            console.log('doInit: '+Ex);
        }
 },
    doneRendering : function(component, event, helper) {
        try{
   $("#afterLoad").html("VALUE CHANGED!!!");
            console.log('doneRendering: Success');
        }catch(Ex){
            console.log('doneRendering: '+Ex);
            
            setTimeout(function(){
                try{
                    $("#afterLoad").html("VALUE CHANGED!!!");
                    console.log('doneRendering-Timeout: Success');
                }catch(Ex){
                    console.log('doneRendering-Timeout: '+Ex);
                }
            }, 100);
        }
 }
})

BlogScriptAppRenderer.js

({
 afterRender : function(){
        this.superAfterRender();
  try{
   $("#afterLoad").html("VALUE CHANGED!!!");
            console.log('afterRender: Success');
        }catch(Ex){
            console.log('afterRender: '+Ex);
        }
  
    }
})

This is what I get in the console

doInit: ReferenceError: $ is not defined
afterRender: ReferenceError: $ is not defined
doneRendering: ReferenceError: $ is not defined
doneRendering-Timeout: Success 

This means that in the last app event (aura:doneRendering) we don’t have the libraries loaded, and that the only way to do it is to detach from the current execution and use the “setTimeout” method to call asynchronously the needed code.No surprise that this could not work if the jQuery library took too long to loadOne of the suggestions was to use RequireJS on the app, but the problem is the same: if the external scripts are not loaded, the “require” method does not exists and you cannot load its configuration.In this case RequireJS would allow to load in the correct order all the libraries (for instance jQuery, than bootstrap, then another lib …), like in this example:

BlogRequireJSApp.app

<aura:application>
    <aura:handler event="aura:doneRendering" action="{!c.doneRendering}"/>
    <script src="/resource/RequireJS" ></script>    
    <div id="afterLoad">Old value</div>
</aura:application>

BlogRequireJSAppController.js

({
 doneRendering : function(component, event, helper) {
        try{
            helper.loadRequire(component);
            console.log('doneRendering: Success');
        }catch(ex){
            console.log('doneRendering: '+ex);
            setTimeout(function(){
                try{
                    helper.loadRequire(component);
                    console.log('doneRendering-Timeout: Success');
                }catch(ex){
                 console.log('doneRendering-Timeout: '+ex);
                }
            }, 100);
        }
 }
})

BlogRequireJSAppHelper.js

({
    loadRequire : function(component) {
        require.config({
            paths: {
                "jquery": "/resource/BlogScripts/jquery.min.js?",
                "bootstrap": "/resource/BlogScripts/boostrap.min.js?"
            }
        });
        
        require(["jquery"], function($) {
            require(["bootstrap"], function(bootstrap, chartJS) {
                $("#afterLoad").html("VALUE CHANGED!!!");
            });
        });
    }
})

This is what I get in the console

doneRendering: ReferenceError: require is not defined
doneRendering-Timeout: Success 

This way you’ll have the scripts loaded in the correct order usign the RequireJS library: anyway if the RequireJS library is not yet loaded (it depends by your data connection) you’ll see another exception at the end of the log

Here comes the Salesforce community!

I posted a question on the SF developer forums and got a super cool response.From that response I came up with a simple solution that uses dynamic script loading and one event fired: this solution is a simpler reinterpretation of the forum’s one to make it easier to understand.

BlogRequireJSDinamic.app

<aura:application>
        <aura:handler event="forcelogic2:BlogRequireJSEvent" action="{!c.initJS}"/>
        <aura:registerEvent type="forcelogic2:BlogRequireJSEvent" name="requireJSEvent"/>
        <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
        <div id="afterLoad">Old value</div>
    </aura:application>

BlogRequireJSDinamicController.app

({
    /*
        Sets up the RequireJS library (async load)
    */
    doInit : function(component, event, helper){
        
        if (typeof require !== "undefined") {
            var evt = $A.get("e.forcelogic2:BlogRequireJSEvent");
            evt.fire();
        } else {
            var head = document.getElementsByTagName('head')[0];
            var script = document.createElement('script');
            
            script.src = "/resource/RequireJS"; 
            script.type = 'text/javascript';
            script.key = "/resource/RequireJS"; 
            script.helper = this;
            script.id = "script_" + component.getGlobalId();
            var hlp = helper;
            script.onload = function scriptLoaded(){
                var evt = $A.get("e.forcelogic2:BlogRequireJSEvent");
                evt.fire();
            };
            head.appendChild(script);
        }
    },
    
    initJS : function(component, event, helper){
        require.config({
            paths: {
                "jquery": "/resource/BlogScripts/jquery.min.js?",
                "bootstrap": "/resource/BlogScripts/boostrap.min.js?"
            }
        });
        console.log("RequiresJS has been loaded? "+(require !== "undefined"));
        //loading libraries sequentially
        require(["jquery"], function($) {
            console.log("jQuery has been loaded? "+($ !== "undefined"));
            require(["bootstrap"], function(bootstrap) {
                console.log("bootstrap has been loaded? "+(bootstrap !== "undefined"));
                $("#afterLoad").html("VALUE CHANGED!!!");
            });//require end
        });//require end
    }
})

This is what I get in the console

RequiresJS has been loaded? true
jQuery has been loaded? true
bootstrap has been loaded? true 

What has happened?When the app loads (init event) the doInit function tries to understand if the RequireJS library has been loaded. If so fires a BlogRequireJSEvent event. If not yet loaded, dynamically creates a <script> tag with the path to RequireJS, attaching an onload handler, which in fact will inform that the library has been loaded with the same event.The same app is also an handler for the BlogRequireJSEvent event trhough the initJS function: it will load sequentially jQuery and Bootstrap libraries: this way you are pretty sure libraries will be loaded in the correct order.The next step in the solution given in the forums is to make a component that does all the work and fires an event that can be handled from anywhere in your app / components set.All the code above has been packaged into a GitHub repository.Enjoy!

[Salesforce] Practical guide to setup a LiveAgent

I’ve created a simple guide to set up a Live Agent on an ORG with a custom console app.
This is the link: Live_Agent_base_configuration.pdf .
This is a really simple guide believe me and no further explaination is given, beucase the only aim is to make Live Agent working.

Let me know what you think!

May The Force.com be with you!

System.debug(‘TCO13’); //The event log

Day 1 – The arrival

I arrived in Washington D.C. at 5 p.m., after 10 hours and a half of plane and more than 1 hour on line at the customs: no need to say I was really tired.
That’s why the first day in Washington hasn’t been that good or exciting, no meeting with anyone, no TCO13 clues (except for the guys that got me at the airport), 6 hours of jet lag that apparently got me down (don’t ask me why, I survived to 11 hours jet lag in Polinesia without problems, even 9 hours in San Francisco, but with Washington as well as New York I needed 1 day to make the journey comfortable).
The only weird thing to note is what happened at the customs.
Officer: “Why are you staying in D.C.?”
Me: “For a nerd convention?”
Officer: “What??”
Me: “A convention of nerds about computer programming…I’m a computer programmer (and so I’m a nerd n.d.r.)”
Officer: [small laugh]
Me: [small laugh]
Officer: [checking my passport]
Officer: [bigger laugh]
Me: “What?”
Officer: “HAHA that’s weird!”
Me: “But that’s what it is!”
End of the story: I think the officer was a kind of bully when he was young, that’s why he was laughing repetedly…fonrtunately he let me pass!

Day 2 – The wheel we will spin

Nothing seemed to happend until I met @eucuepo on the street back to the hotel (I recognized him at first site, despite my difficulty to remember faces!).
Finally I met the great Tim Hicks (as I said in previous post, he’s my Cloudspokes bro).
It’s been love at first sight…we see eachother from distance and we run each other in time lapse…jokes apart, has been really cool to meet him after one year of emails, hangouts, blog posts and whatever happened!
At noon the jet lag seemed to gain power again, so I decided to take a sleep, but at about 4 p.m. I received a series of email/tweets that actually woke me up! It was @logonkartik who asks if we can meet downstairs at the hotel bar. That’s where I met him with @chok68, and finally the Cloudspokes crew started to grow up!

This is some nerd swag I got:

After few chats (trying to speak English better than I could) the crew added more guys: Kyle Bower, Cory McIlroy, Dave Messinger, Chris DeLaurentis, Mike Cardillo, @CloudBytes and George Acker for the Cloudspokes mith part, and @callmecatootie, @jan3594, @wcheung, @soe, @aproxcacs …I know I’m forgetting the majority!

I also had the pleasure to met Narinder Singh, the CEO/CIO of Cloudspokes.

At 7.30 pm TCO13 opens for the starting party (NASA, Google, Facebook among sponsors!!) and I finally had to spin the wheel. I got:

  • FinancialForce API
  • Amazon Webservices
  • Docusign

But still I have no hint of what I’m gonna do!

Day 3 – The infinite sleep

I know I’m on vacation, I’m in the US, I’m nerding like there won’t be a tomorrow, but today I’m really tired.
The only notable thing was that I met (finally) Jeff Douglas.

After a 2h city walk, I felt I need more concentration because of the lack of sleep, so I decided to close myself in the hotel room and start coding there, and that’s gone pretty cool.
I’ve coded from 12 pm to 7 pm and finally, together with the other guys, I went to the Faceook party at their Washington Offices: a big open space, with some nerdy things (Nintendo 64, Nerf guns for example), a big wall (I left my sign on it ) and cool Facebook guys that exaplined to us some things about their work and office (like why a room is called beofre “Al Gore”…just because one day the vice president went in their office and entered in that room and a guy said “Yay!” to greet him…”and that’s why this is the Al Gore room!”).
After that some other lines of code (messing with DocuSign), and I’m ready to dive in the bed (hopefully I’ll dream an entire night).

Day 4 – The coding day

Nothing notable today…coding from 5 am to 4 am (yes about 23 hours…just lunch and dinner time of break).
This is what I came up with: Sheherazade – The crowdsourcing story teller.

I tried to apply crowsourcing development in a less technical field that is the story telling. What I wanted to achieve was a sort of ideas generator (for plots, song lyrics, books made of pictures) for editors, rewarding users by money prices and comunity glory.

For a complete overview of the app see these demo videos:

  1. http://www.screencast.com/t/jzKwI389rfDJ
  2. http://www.screencast.com/t/nlIoJtgDmS
  3. http://www.screencast.com/t/hwNeUuR2pG8
  4. http://www.screencast.com/t/m7JJrwZ21a2n
  5. http://www.screencast.com/t/hUaaft0WK4qu

And these are the slides of the presentation.
I chose to code alone in my hotel room because of concentration and because in the main hall of the event I had to sit in pouf chairs not that comfortable, and what I wanted to do was a bit complicate and need the most of the time I could have.

Day 5 – Judgment and winner announcement

I slept about 3 hours to get the app ready, and made a funny presentation of my wedding day using the app, to show how a plot can be easily constructed using the comunity as a source of ideas.
This was the scaring jury:

This is the story: “…And they lived happily ever after…” .

This is me presenting the app (I think I was saying something really stupid…this is my “What did I say?!?” face ):

Than, after a 1 hour sleep, came the second presentation, to a bigger audience:

In a flash of an eye we were about to know the winners….and I got 4th place! Not bad! I think the main reason of the missed first place is that my APIs were hidden while the other solutions actually created “something new” with theier APIs (and in a really smart way…so congrats to my Cloudspokes buddies!!)…you can read in the following Jeff Douglas’s post the results and the video demo of the winner apps: The TCO13 Cloud Mashathon – Building Killer Apps with the API “Wheel of Fortune”.

I think this pic summarizes the coolness and amazingness and awesomeness of the whole TCO13:

This has been the most valuable professional expirience I’ve ever had, I could meet a lot of smart people from all over the world and compete with them in a friedly way (you know money makes people fighting each other), and I can say I was happy for my Cloudspokes buddy win, I recognize their value and great skills!
I really hope to be part of the TCO14, and I know I have to work hard to achieve this!
Lastly I want to thank all Cloudspokes and Topcoder staff for the opportunity they gave me and to make me feel a bit special!

This is the final video event:

[NodeJS + Salesforce SOAP WS] How to consume a Salesforce SOAP WSDL

I was wondering how to consume Salesforce WSDLs with nodejs.
I found Node Soap package (see npm) and I tried to consume a Partner WSDL.
Then I saved the WSDL in the “sf-partner.wsdl” file and played with the methods to get nodeJS speak SOAP with Salesforce.

var soap = require('soap');
var url = './sf-partner.wsdl';
soap.createClient(url, function(err, client) {
   console.log('Client created');
   console.log(client.SforceService.Soap); //all methods usable in the stub
});

If you try to console.log(client) you will see too much data

This is the output:

{ login: [Function],
  describeSObject: [Function],
  describeSObjects: [Function],
  describeGlobal: [Function],
  describeDataCategoryGroups: [Function],
  describeDataCategoryGroupStructures: [Function],
  describeFlexiPages: [Function],
  describeAppMenu: [Function],
  describeGlobalTheme: [Function],
  describeTheme: [Function],
  describeLayout: [Function],
  describeSoftphoneLayout: [Function],
  describeSearchLayouts: [Function],
  describeSearchScopeOrder: [Function],
  describeCompactLayouts: [Function],
  describeTabs: [Function],
  create: [Function],
  update: [Function],
  upsert: [Function],
  merge: [Function],
  delete: [Function],
  undelete: [Function],
  emptyRecycleBin: [Function],
  retrieve: [Function],
  process: [Function],
  convertLead: [Function],
  logout: [Function],
  invalidateSessions: [Function],
  getDeleted: [Function],
  getUpdated: [Function],
  query: [Function],
  queryAll: [Function],
  queryMore: [Function],
  search: [Function],
  getServerTimestamp: [Function],
  setPassword: [Function],
  resetPassword: [Function],
  getUserInfo: [Function],
  sendEmailMessage: [Function],
  sendEmail: [Function],
  performQuickActions: [Function],
  describeQuickActions: [Function],
  describeAvailableQuickActions: [Function] }

There is a quicker way to obtain this using the console.log(client.describe()) function, but this seems not to work with big WSDL like Salesforce ones (Maximum stack error)
The first move was to login to obtain a valid session id (using SOAP login action):

soap.createClient(url, function(err, client) {
    client.login({username: '[email protected]',password: 'FreakPasswordWithTkenIfNeeded'},function(err,result,raw){
      if(err)console.log(err);
      if(result){
          console.log(result.result);
    });
});

And this is the result

{ metadataServerUrl: 'https://na15.salesforce.com/services/Soap/m/29.0/00Di0000000Hxxx',
  passwordExpired: false,
  sandbox: false,
  serverUrl: 'https://na15.salesforce.com/services/Soap/u/29.0/00Di0000000Hxxx',
  sessionId: 'XXXXXXXXXX',
  userId: '005i0000000MXXXAAC',
  userInfo: 
   { accessibilityMode: false,
     currencySymbol: '€',
     orgAttachmentFileSizeLimit: 5242880,
     orgDefaultCurrencyIsoCode: 'EUR',
     orgDisallowHtmlAttachments: false,
     orgHasPersonAccounts: false,
     organizationId: '00Di0000000HxxxXXX',
     organizationMultiCurrency: false,
     organizationName: 'Challenges Co.',
     profileId: '00ei0000000UM6PAAW',
     roleId: {},
     sessionSecondsValid: 7200,
     userDefaultCurrencyIsoCode: {},
     userEmail: '[email protected]',
     userFullName: 'Admin',
     userId: '005i0000000MxxxXXX',
     userLanguage: 'en_US',
     userLocale: 'en_US',
     userName: '[email protected]',
     userTimeZone: 'Europe/Rome',
     userType: 'Standard',
     userUiSkin: 'Theme3' } }

Now the problem was to put the new endpoint and the session id or the next call, and this is the solution:

  //sets new soap endpoint and session id
  client.setEndpoint(result.result.serverUrl);
  var sheader = {SessionHeader:{sessionId: result.result.sessionId}};
  client.addSoapHeader(sheader,"","tns","");

And after that you can make wathever call you want:

      client.query({queryString:"Select Id,CaseNumber From Case"},function(err,result,raw){
          if(err){
            //console.log(err);
            console.log(err);
          }
          if(!err && result){
            console.log(result);
          }
      });

The result var will have all the data you expect from the SOAP response:

{ result: 
   { done: true,
     queryLocator: {},
     records: 
      [ [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object] ],
     size: 26 } }

The fact that you cannot call the client.describe() force us to read the WSDL to know which parameters send to the call.

[Salesforce / Canvas / NodeJS] Set up a minimum Canvas App (NodeJS style)

Following the old post [Salesforce / Canvas] Set up a minimum Canvas App (Java style) I’ve decided to put togheter the code I’ve used to talk to a Force.com Canvas App in NodeJS.

For those who can’t wait the whole article, this is the repo (CanvasApp-NodeJS-Base).

Follow this steps to configure an App (something is changed in the last SF releases):

  • Setup -> Create -> Apps -> (Connected Apps) New
  • Fill Conntected App Name, API Name and Contact Email
  • Check the Enable OAuth Settings in the API (Enable OAuth Settings) section
    • Callback URL: put a valid URL (you don’t need it for the scope of this example)
    • Selected OAuth Scopes: select the scopes you want for the NodeJS app acess token to grant
  • In the section Canvas App Settings type:
  • Canvas App URL: the url of the POST action to be called by Salesforce when injecting the session data (e.g. https://my-canvas-app.herokuapp.com/canvas/callback)
  • Access Method: use Signed Request (POST)
  • Locations: choose Chatter Tab (the app will be shown in the Chatter page)

Than you need to enable this app:

  • Setup -> Manage Apps -> Connected Apps -> Your App Name
  • In the OAuth policies select Admin approved users are pre-authorized for the Permitted Users field
  • In the Profiles related list select your profile (all user of that profile are automatically allowed to consume this app)

There are more ways to configure the Canvas App usage, this is the quicker.
Now clone this github repo CanvasApp-NodeJS-Base and create your own app:

$ cd [your test folder]
$ git clone https://github.com/enreeco/CanvasApp-NodeJS-Base
$ heroku create my-canvas-app
$ git push heroku master

The app will be pushed to Heroku (if you are using Heroku 🙂 ) and you will find it listening @ https://my-canvas-app.herokuapp.com.
Remember to use the “https” protocol in the callback URL setting, otherwise the canvas app won’t work correctly when called from Salesforce (using iframes with different protocols makes the browser go creazy).

To make it work you have to set the secret as an Environmental variable (the secret is the Consumer secret field in the App settings).
On Heroku simply type:

$ heroku config:set SF_CANVASAPP_CLIENT_SECRET=XXXXXXXXX

If you access the app outside the canvas this is what you see:

This is what happens in case of error:

This is what happens if all goes well:

The core of this app is the sf-tools/index.js file which handles the decoding of the POST request that contains all the canvas info (such as token, urls, …): have a look at the verifyAndDecode(input, secret) function.
You can then use this token and urls to make whatever call you want to your instance (as long as your user has access to it), but its outside of this post!

I leave you with a picture of a cute cat, this sould bring here a lot more readers.

The unwritten guide (or the untold story, you chooose) of a good (badass) Cloudspoker

Here I am, 1 year and a few weeks have passed from the first time I joined Cloudspokes (there have also been a second time…but it’s another story).
Look for me on the site, I’m known as ForceLogic (former known as Enreeco…you got it, I have 2 accounts..this is the same “other story”).

This is the very first community in which I take a significant part: it’s not because I’m antisocial, solitary or because I speak a terrible English, but only because I loose interest in the things I do really quickly.

Things changed when I met Cloudspokes.
It seemed strange that I could be paid for my Force.com knowledge, for things I usually did (and currently do) at work, but being away thousands miles.
At first I thought I was not able to compete, and seeing the mith of Jeff Douglas (talking about Salesforce, his blog was constantly one of my landing pages when googling about Force.com) made me think I cloudn’t loose time in things I couldn’t handle.

I was absolutely wrong!

Lucky thing is that I bookmarked the site and, randomly, one month later I decided to come in and examine in depth what Cloudspokes really was.

A new world opened to me! I started coding in the weekends or during the nights, learning whatever technology I wanted to learn (there is so much choice you can choose the techs you prefer!).
I already was a good Force.com developer but the Cloudspokes experience incresed of a significant 30% my analysis and coding speed and quality, and made me learn part of the Force.com platform that I didn’t know.
Once I felt confident with Force.com challenges, I started with other technologies (pure JS client scripting learning different JS libraries, server side programming in Java, desktop programming, HTML5), ending with my favorite techs (as of now), AngularJS and NodeJS and Bootstrap to give a nice style (I’m an engineer and usually I make the things working without attention to their beauty…believe in me, libraries such as Bootstrap makes you feel a good web developer), thanks to Jeff, Dave, Mike and Kyle, the first Cloudspokes masters I met at the beginning of my journey, with the great support of Tim (my Cloudspokes Bro).

They are not the only ones I have met in this year, but I cannot name all this great guys one by one (and you’ll meet them when you’ll join the cause).

The real question is: What is Cloudspokes?

A community?

A developers hub?
A bunch of mercenary nerds?
A place for gods of web development?
A showroom of cool code?
The Devil (when I’m completely absorbed by a challenge I actually think it is!)?

The best answer I can give is: Cloudspokes is a constant challenge!

You can be a very noob and you win a challenge even if you are competing with the best of the bests!

Suggestion number 1: Never be afraid to compete!

In this scenario, every one must give 100% of his capabilities, from 50$ projects to 1M$ projects (just kidding, no million dollar challenge…as of now!), nothing is certain, once you lower your guard, altough you are the very best, you can loose with shame (It happened to me more than once!).

Suggestion number 2: code each challenge with the same passion and frenzy as it is the first you do!

Remember that you are not the only one competing and think that all the other submissions will be at least as good as yours, so try to enhance the solution with cool stuff, document your code and make cool video demo (“a video worth a thousand documentation pages“, cit. @eucuepo).

Suggestion number 3 (only for non English speakers with an English knowledge as low as mine): don’t be shy!

Make a video speaking English, in 2 or 3 video you will acquire confidence (even if reinventing English grammar) and you will please the judges (seeing a solution running is different than only reading a doc!).

Suggestion number 4: have fun, sometimes leave apart your familiar skills and dive into something you actually don’t know (I made an AngualarJS + NodeJS challenge without even knowing anything).

You may hate what you learned or loose badly the challenge, but at least you are now aware of that tech, and believe me this is a great success for your skills!

Suggestion number 5: during challenges, make questions as much as you can, even if it makes you feel dumb. It can happen that your “dumb” question triggers a thread of cool stuff or ideas or new point of views.

Suggestione number 6: always complete your tasks but if you can’t do it (sometimes could also be a matter of time…we have a life!) tell it to the admins and submit anyway. Something cool could be present in your code that could be used away. This doesn’t mean that you have to submit every garbage you produce, so try to figure out if what you did could have at least a single bit of value!

Suggestione number 7: when you are “feeling down” because you are not kicking asses, take a deep breath, let it out slowly, take a break, take another breath, relax your mind…the next time will be better! Cloudspokes is growing up, so there will be more and more competition but you will grow up with it and you’ll being soon to master the technique of being a badass Cloudspoker.

Suggestion number 8: join actively the community and try to know other Cloudspokers, the fact that you compete with them doesn’t mean you are enemies! And watching their submissions is another source of power (feel like a vampire).

Suggestion number 9: I can’t find a 9th point, but cool lists on the web have at least 10 points

Suggestion number 10: if you haven’t already done, join us!

[Github / Maven] Maven repository using GitHub

This simple post is a reminder on how to create a Maven2 repository and use it with Maven in a “pom.xml” file or in Java Play!. The first step is to create a GitHub Repository (it will be named “maven2”).Then reproduce the folder structure of a Maven Repository, as I did in my “Maven2” repo (https://github.com/enreeco/maven2).For instance, given this artifact (jar):

<groupId>com.rubenlaguna</groupId>
  <artifactId>evernote-api</artifactId>
  <name>Official Evernote API</name>
  <version>1.22</version>

Create these folders:

./com
     ./com/rubenlaguna
     ./com/rubenlaguna/evernote-api
     ./com/rubenlaguna/evernote-api/1.22
     ./com/rubenlaguna/evernote-api/1.22/evernote-api-1.22.jar
     ./com/rubenlaguna/evernote-api/1.22/evernote-api-1.22.pom

Now you have a pubblicly accessible Maven2 repository at https://github.com/[username]/[repoName]/raw/master/ (in my case https://github.com/enreeco/maven2/raw/master/).To consume it in your projects, just add this lines in the “pom.xml” file of your project:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.echoservice</groupId>
    <version>1.0-SNAPSHOT</version>
    <artifactId>echoservlet</artifactId>
    <repositories>
    <repository>
      <id>myRepository</id>
      <url>https://github.com/enreeco/maven2/raw/master/</url>
    </repository>
    </repositories>
    <dependencies>
 . . .
 </depencencies>
 . . .
</project>

To integrate the maven repo in a Java Play! project (necessary if you’re messing with Heroku), take the “/prject/Build.scala” file and add those lines:

import sbt._
import Keys._
import PlayProject._

object ApplicationBuild extends Build {

    val appName         = "evernote-integration"
    val appVersion      = "0.1"

    val appDependencies = Seq(
      "com.rubenlaguna" % "evernote-api" % "1.22", //Evernote Thrift Library
      "com.rubenlaguna" % "libthrift" % "1.0-SNAPSHOT",//Thrift core library
      "postgresql" % "postgresql" % "9.1-901.jdbc4", //Postgres
      "org.apache.httpcomponents" % "fluent-hc" % "4.2.1" //Apache HTTP client
    )

    val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings(
     resolvers+= "personal maven repo" at "https://github.com/enreeco/maven2/raw/master/"
    )

}

Maven will search for the selected jars in the default locations, but when it’s not finding anything it will search for your personal resolvers.

[Visualforce / Tip] Always add a tag

Always add <apex:pagemessages/> at the beginning of your VF page to see if there are errors in loading/submitting VF page.
If something underneath the Visuarlfoce is wrong, you don’t see anything (no clear log at least)!

I was using the automatic SOQL generation of the “Object__c” standard controller, and to do this I usually use (well, I used to do it in the past, now I only use a series of “{!field}” in a hidden <apex:outputPannel>):

     <apex:inputHidden value="{!Object__c.CreatedDate}" />
     <apex:commandButton value="Do something" action="{!doSomething}"/>

This is clearly a blasphemy,because you cannot set with input hidden a “created date” field, but as I hadn’t wrote this piece of code myself, I didn’t see it immediatly.

Result was that commandLink/ButtonLink was not working, no log provided (exception made for the page load’s initialization debug log), everithing I did made the button simply reloading the page without a real postback.

Using a simple <apex:pagemessages/> I saw the error and manage to know what was the problem.

[Apex / JavaScript] What is @RemoteAction? Baby don’t hurt me, don’t hurt me, no more!

2 hours to find out why @RemoteAction was hurting me.
This is related to Cloudspoke’s challenge POC – Bootstrap Visualforce Pages (which I haven’t submitted neither).

This is the controller:

@RemoteAction
    public static List queryContacts(List filters, String orderBy) {
    ...
    }

And this is the piece of javascript:

  var orderBy;
  var filters = [];
  function searchContacts()
  {
   var ob = orderBy;
   Visualforce.remoting.Manager.invokeAction(
              '{!$RemoteAction.MyPageController.queryContacts}',filters,ob,
              function(result, event){
               
                  if (event.status) {
                   
                   //result has the List of contacts
                   var contacts = result;
                   console.log(contacts);
                  } else if (event.type === 'exception') {
                       alert(event.message);
                  } else {
                    alert(result+ ' '+event.message);
                  }
              }, 
              {escape: true});
  }

It keeps sayng:

Visualforce Remoting Exception: Method 'queryContacts' not found on controller CSChallenge.MyPageController. Check spelling, method exists, and/or method is RemoteAction annotated.

The problem is the javascript variable “orderBy” that is “undefined”!!

By initializing it with:

     var orderBy = '';

@RemoteAction go smoothly.


Courtesy of Doombringer

Page 26 of 27

Powered by WordPress & Theme by Anders Norén