Wednesday, 9 May 2018

Salesforce: Making API calls from Lightning Components

We can use REST api in Lightning components by using server side controller, because you can't make API calls from client-side code.

Let's take one example to display weather on your Account records, based on address fields on Account records.

we are going to use the Api to get weather from this URL https://openweathermap.org/api.

Just sign up from this URL to get the weather key. https://home.openweathermap.org/users/sign_up

Once you logged in, click on API Keys tab, like shown below.



It will provide one Default Key, save this Key for future use.

Create Remote site setting in your salesforce org. 

Setup | Security Controls | Remote Site Settings | New

Use this URL http://api.openweathermap.org



Now you are all set to write code.

Create a apex class WeatherController.cls in your salesforce org.



public with sharing class WeatherController {

    private static String apiURL = 'http://api.openweathermap.org/data/2.5/';

    private static String apiKey = 'use your API key ';

    private static String pathCurrentWeather = 'weather?appid='+apiKey+'&units=metric&q=';
    
    // Pass in the endpoint to be used using the string url
    private static String getCalloutResponseContents(String url) {
        String result = null;
        // Instantiate a new http object
        Http h = new Http();
        
        // Instantiate a new HTTP request, specify the method (GET) as well as the endpoint
        HttpRequest req = new HttpRequest();
        req.setEndpoint(url);
        req.setMethod('GET');
        
        // Send the request, and return a response
        HttpResponse res = h.send(req);
        if(res.getStatusCode() == 200){
            result = res.getBody();
        }
        return result;
    }

    @AuraEnabled
    public static Object currentWeather(Id recordId) {
        String city;
        if (recordId!=null && recordId.getSobjectType() == Schema.Account.getSObjectType()) {
            Account acc = [Select Id, BillingCity, BillingCountry, BillingLongitude, BillingLatitude from Account WHERE Id=:recordId];
            if (acc!=null) {
                city= acc.BillingCity+' ,'+acc.BillingCountry;
            }
        }
        else if (recordId.getSobjectType() == Schema.Contact.getSObjectType()) {
            Contact con = [Select Id, MailingCity, Mailingcountry from Contact WHERE Id=:recordId];
            if (con!=null) {
                city= con.MailingCity+' ,'+con.MailingCity;
            }
        }
        String weatherUrl = apiURL + pathCurrentWeather + city;
        System.debug('weatherUrl::' + weatherUrl);

        String result = getCalloutResponseContents(weatherUrl);
        System.debug('result::' + result);

        return (result == null)?null:getCalloutResponseContents(weatherUrl);
    }
}


Create Lightning Component:


<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,force:hasRecordId" controller="WeatherController" >
    <aura:attribute name="weather" type="Object" />
    <aura:attribute name="message" type="String" />
    <aura:attribute name="title" type="String" />
    <aura:attribute name="severity" type="String" />
    <aura:attribute name="recordId" type="Id" />
    <aura:handler name="init" value="{!this}" action="{!c.getCurrentWeather}" />
    
    <ltng:require styles="{! $Resource.SLDS203 + '/assets/styles/salesforce-lightning-design-system.min.css'}" />
    <div class="slds-grid slds-wrap slds-grid--pull-padded">
        <div class="slds-col--padded slds-size--1-of-1">
            <ui:message class="slds-hide" title="{!v.title}" severity="{!v.severity}" closable="true" aura:id="message">
                {!v.message}
            </ui:message>
            
        </div>
        <div class="slds-col--padded slds-size--1-of-1">
            <aura:if isTrue="{!v.weather != null}">
                <div class="slds-page-header" role="banner">
                    <div class="slds-grid">
                        <div class="slds-col slds-has-flexi-truncate">
                            <div class="slds-media slds-media--center slds-no-space slds-grow">
                                <div class="slds-media__body">
                                    <p class="slds-text-heading--label">City</p>
                                    <h1 class="slds-page-header__title slds-m-right--small slds-truncate slds-align-middle" title="Record Title">
                                        {!v.weather.name},&nbsp;{!v.weather.sys.country}
                                    </h1>
                                </div>
                            </div>
                        </div>
                    </div>
                    <ul class="slds-grid slds-page-header__detail-row">
                        <li class="slds-page-header__detail-block">
                            <p class="slds-text-heading--label-normal slds-truncate slds-m-bottom--xx-small" title="Current">Current</p>
                            <p class="slds-text-body--regular slds-truncate">{!v.weather.main.temp}&deg;C</p>
                        </li>
                        <li class="slds-page-header__detail-block">
                            <p class="slds-text-heading--label-normal slds-truncate slds-m-bottom--xx-small" title="Minimum">Minimum</p>
                            <p class="slds-text-body--regular slds-truncate">{!v.weather.main.temp_min}&deg;C</p>
                        </li>
                        <li class="slds-page-header__detail-block">
                            <p class="slds-text-heading--label-normal slds-truncate slds-m-bottom--xx-small" title="Minimum">Maximum</p>
                            <p class="slds-text-body--regular slds-truncate">{!v.weather.main.temp_max}&deg;C</p>
                        </li>
                        <li class="slds-page-header__detail-block">
                            <p class="slds-text-heading--label-normal slds-truncate slds-m-bottom--xx-small" title="Longitude">Longitude</p>
                            <p class="slds-text-body--regular slds-truncate">{!v.weather.coord.lon}</p>
                        </li>
                        <li class="slds-page-header__detail-block">
                            <p class="slds-text-heading--label-normal slds-truncate slds-m-bottom--xx-small" title="Latitude">Latitude</p>
                            <p class="slds-text-body--regular slds-truncate">{!v.weather.coord.lat}</p>
                        </li>
                        <li>
                        <img src="{!$Resource.weather}" style="max-width:30%"></img>
                        </li>
                    </ul>
                </div>
                
            </aura:if>
        </div>
    </div>
</aura:component>

Javascript controller:



({

getCurrentWeather : function(component, event, helper) {

//var city = component.find('city').get('v.value');

helper.getCurrentWeather(component, helper,event);
}
})



Helper:



({

getCurrentWeather : function(component, helper, city) {

var action = component.get('c.currentWeather');

//action.setParams({"city": city});
        action.setParams({recordId:component.get("v.recordId")});
action.setCallback(this, function(response) {
var state = response.getState();

if (state === "SUCCESS") {
var result = response.getReturnValue();
console.log(result);
if(result != null){
result = JSON.parse(result);
if(result.cod == 200){
component.set('v.weather', result);
}else{
helper.message(component, 'Error!', 'error', result.message);
}
}else{
helper.message(component, 'Error!', 'error', 'Some error occured while getting weather data');
}
}else if (state === "INCOMPLETE") {
}else if (state === "ERROR") {
var errors = response.getError();
if (errors) {
if (errors[0] && errors[0].message) {
helper.message(component, 'Error!', 'error', errors[0].message);
}
} else {
helper.message(component, 'Error!', 'error', 'Unknown Error');
}
}
});
$A.enqueueAction(action);
},
message: function(component, title, severity, message){
component.set('v.message', message);
component.set('v.title', title);
component.set('v.severity', severity);
var ele = component.find('message');
$A.util.removeClass(ele, 'slds-hide');
$A.util.addClass(ele, 'slds-show');
}
})


Execution Time:


Open any Account record in Lightning mode --> Edit page --> Like shown below



Drag and drop your weather Lightning component to header section and don't forget to provide valid Billing address in your account record.

Then your work is done! weather displays in your record like this.



Done

Tuesday, 8 May 2018


Lightning Out:

Lightning Components on external websites – Lightning Out

Steps to follow the Lightning components to display in your local host by using Lightning out, without logging into sales force.


Step 1:

Create below Lightning application in your Salesforce instance and name it as TestApp
<aura:application access="Global" extends="ltng:outApp">
    <aura:dependency resource="c:Currencyconvert"/>
</aura:application>
Here Currencyconvert is Lightning component, you can use any component that you have in your instance.

Step 2:

Now NodeJs and OpenSSL you must install in your pc, see below

       Download and install nodejs application latest version from the following URL.





Set nodejs path in your system Environment variables.
 Copy & Paste the path from program files where nodejs is installed.





Test nodejs application is installed correctly or not!
Open your command prompt -> type Node
It will take your inputs and executes, like show below




Use twice Cntrl+C to exit.

          Download & install OpenSSL from the following URL.



            

Even If you have 64-bit operating system also, still the above link Win32OpenSSL works.

After installation it will create one folder in C drive, then add the path of the bin folder in your environment variables.
              
   



Test now, open command prompt and they OpenSSL, It should jump to OpenSSL like shown below.



Cntrl+C – to exit



Step 3:-

Create connected app and Auth. Provider in your salesforce instance.

Connected App:-
To create Connected App in “service provider Salesforce instance”, Navigate to “Setup | Build | Create | Apps | Connected Apps” and click on New.
Provide All information except “Callback URL”(use some dummy url bcz it’s required). We will come back again on this step later to provide Callback URL.
See the below screen shot for reference.


Once you save this Connected app, it will provide “Consumer Key” and “Consumer Secret”, we need this information in next step.

Create Authorization Provider:-
Create Authorization Provider in “Authentication provider Salesforce instance”.
Navigate to “Setup | Administer | Security Controls | Auth. Providers | Create New”.
Select “Salesforce” as provider Type.

We need to provide “Consumer Key” and “Consumer Secret” created in previous step.

Authorize Endpoint URL should be something like
“https://AuthenticationProviderinstance/services/oauth2/authorize”
And Token Endpoint URL “https://AuthenticationProviderinstance/services/oauth2/token“.

Click on “Automatically create a registration handler template”, it will generate one apex class. Then Select User who should be used to execute this Apex class when user tries to login.




Once you save “Auth. Provider” in previous step, it will provide you list of URL as shown in above image. Copy Callback URL and edit Connected App we just created and set this URL.

Update Auto generated Registration Handler Apex class
Replace content of Apex class by below code,

global class AutocreatedRegHandler1525398009718 implements Auth.RegistrationHandler{
    class RegHandlerException extends Exception {}
    global User createUser(Id portalId, Auth.UserData data){
        try{
            User u = [SELECT ID FROM User Where Other_Salesforce_Org_Username__c = : data.username];
            return u;
        } catch (Exception e){
            throw new RegHandlerException('Cannot find this user. For help, contact your administrator.');
        }
    }
    
    global void updateUser(Id userId, Id portalId, Auth.UserData data){
        
    }
}
Note: Create required fields used in above class.

Add Salesforce button on Login Page
Navigate to “Setup | Administer | Domain Management | My Domain | Authentication Configuration | Edit”.



Note: Auth. Provider we are using for to create OpenSSL certificate.

Step 4:
Create Salesforce CORS in your salesforce instance with URL: https://localhost:8081
Setup | Security controls| CORS



Step 5:
Download source code from github repository from the following blog.



Modify the html code that you have downloaded from the above URL.

Goto Viewsà Main.html à Open in your notepad
Just modify the following content
<script src="https://chakri-7-dev-ed.lightning.force.com/lightning/lightning.out.js"></script>
(Provide your org URL in above line)
  <script type="text/javascript" src="lib/jquery.js">  </script>
  <script type="text/javascript" src="lib/jqueryCookies.js"> </script>  
  <script type="text/javascript" src="js/Main.js"> </script> 

var appName = "c:TestApp";  (Lightning app that you have created in first step)

$Lightning.createComponent("c: Currencyconvert", {type: type, subjectId: subjectId}, "lexcontainer"); 
(Use your component in above line)

Goto Clientà Js àOAuth.Js à Open in your notepad
clientId = ‘provide your consumer key here’,
    loginUrl = 'https://login.salesforce.com/',
    redirectURI = "http://localhost:8080/oauthcallback.html",
    proxyURL = 'http://localhost:8080/proxy/' ;

Create Main.js file in the above Js folder which is missing
getLoggedUserInfo();
addAPICount();
var $apiEle = $("apiCount");
function getLoggedUserInfo()
{
                var url = $.cookie("idURL");
               
                client.ajex(url,
                                                                                function(data){
                                                                                                $("#loggedInUser").html(data.display_name);
                                                                                },
                                                                                function (error){
                                                                                                console.log(error);
                                                                                },
                                                                                'GET',
                                                                                '',
                                                                                true
                                                                );
}

function addAPICount()
{
                if(!$apiCount)
                {
                                $apiEle = $("apiCount");
                }
                $apiEle.text(parseInt($apiEle.text())+1);
}


You don’t need to modify any other files in that folder, if you wish to modify any UI context, you can modify Index.html file in Views folder.




Step 6:
Download openssl.exe:-
Just download the file from the following URL, and paste it into the Lightning out folder. It will avoid the “No_Oauth_State: State was not sent back” error. (No need to install it)





Create Certificate using OpenSSL
We already installed OpenSSL and NodeJs applications in our system.
Now, Run below commands to create self signed certificate.
openssl req -newkey rsa:2048 -new -nodes -keyout key.pem -out csr.pem
openssl x509 -req -days 365 -in csr.pem -signkey key.pem -out server.crt

Above command will create two files – server.crt and key.pem which needs to be used in Node.js application.


Step 7:
Now go back to the Salesforce instance, and open Connected App, change the call back URL to https://localhost:8081/oauthcallback.html



Step 8: Execution time
Open command prompt run Node.js application to use Lightning Component.
Run below commands in command prompt.
·         npm install
·         npm start



If everything works fine, you will see message on console saying Server listening for HTTPS connections on port 8081 like above screenshot.
Navigate to https://localhost:8081/ from your browser and you will see page asking to login.



Click on Production and allow, it will take you to your Lightning app page.