Connecting Salesforce with Weatherbit.Io APIs using LWC

I am really happy to announce that ways2salesforce jump the step and bring the article on the Integration concept for you. The article “Connecting Salesforce with Weatherbit.Io APIs using LWC” written by Rishabh Kumar.

Use case: Salesforce user (rep/agent/admin) wants to check the temperature and some information related to weather-based on postal code/city & country/latitude & longitude and want the system to integrate with WeatherBit.

Pre-requisite: Some of the pre-requisite terms, need to be done before starting:

  1. Sign-up to the Weatherbit API to get the connection key (required for the integration).
  2. Technologies used: Lightning Web Component, Apex Class, Rest API
  3. Required trailhead playground and VS code configured (set as a default playground).
  4. Create a Remote Site setting.
    • Go to Setup -> Remote Site Settings
    • Click New Remote Site
    • Enter Remote Site Name as ‘Weatherbit’
    • Enter ‘https://api.weatherbit.io’ as Remote Site URL.
    • Make sure Active checkbox is checked.
    • Click Save.

Implementation

WeatherbitLatLongResponse.apxc

public with sharing class WeatherbitLatLongResponse {
public List<Data> data;
public Integer count;

public class Weather {
    public String icon;
    public String code;
    public String description;
}

public class Data {
    public String pod;
    public Double lon;
    public Double pres;
    public String timezone;
    public String country_code;
    public Double clouds;
    public Double solar_rad;
    public String state_code;
    public String city_name;
    public Double wind_spd;
    public String last_ob_time;
    public String wind_cdir_full;
    public String wind_cdir;
    public String sunset;
    public Double dewpt;
    public Double snow;
    public Double uv;
    public Double precip;
    public Double wind_dir;
    public String sunrise;
    public Double lat;
    public Weather weather;
    public String date_time;
    public Double temp;
    public String station;
    public Double app_temp;
}

public static WeatherbitLatLongResponse parse(String json) {
        return (WeatherbitLatLongResponse) System.JSON.deserialize(json, WeatherbitLatLongResponse.class);
    }
}

Weatherbitcontroller.apxc

public with sharing class weatherbitcontroller {

    public class DisplayWeatherDetail{
        @AuraEnabled public String lat;
        @AuraEnabled public String lon;
        @AuraEnabled public Double temp;
        @AuraEnabled public String description;
        @AuraEnabled public String sunrise;
        @AuraEnabled public String sunset;
        @AuraEnabled public String city;
        @AuraEnabled public String time_zone;
    }
    @AuraEnabled(cacheable=true)
    public static DisplayWeatherDetail getWeatherStatus(String latitude, String longitude, String postalcode, String city, String country){
        
        if((String.isNotBlank(latitude) && String.isNotBlank(longitude)) || String.isNotBlank(postalcode) || (String.isNotBlank(city) && String.isNotBlank(country))){
            try {
                HttpRequest req = new HttpRequest();
                req.setMethod('GET');
                req.setHeader('Content-Type', 'application/json');
                if(String.isNotBlank(latitude) && String.isNotBlank(longitude)){
                    req.setEndpoint('https://api.weatherbit.io/v2.0/current?lat='+Double.valueOf(latitude)+'&lon='+Double.valueOf(longitude)+'&key=9764bdf3cf7847e788013cabaf408153');
                }
                else if(String.isNotBlank(postalcode)){
                    req.setEndpoint('https://api.weatherbit.io/v2.0/current?postal_code=' + Integer.valueOf(postalcode) + '&key=9764bdf3cf7847e788013cabaf408153');
                }
                else if(String.isNotBlank(city) && String.isNotBlank(country)){
                    city = city.replaceAll(' ','%20');
                    country = country.replaceAll(' ','%20');
                    req.setEndpoint('https://api.weatherbit.io/v2.0/current?city=' + city + '&country=' + country + '&key=9764bdf3cf7847e788013cabaf408153');
                }
                Http http = new Http();
                HTTPResponse res = http.send(req);
                String str = res.getBody();
                System.debug(' ::;str'+str);
                //parse the JSON body
                WeatherbitLatLongResponse respObj = WeatherbitLatLongResponse.parse(str);
                WeatherbitLatLongResponse.Data respData = respObj.data[0];
                //creating object of inner class to display onto the UI
                DisplayWeatherDetail detailObj = new DisplayWeatherDetail();
                //assigning field value to the inner class fields
                detailObj.lat = String.valueOf(respData.lat);
                detailObj.lon = String.valueOf(respData.lon);
                detailObj.sunrise = respData.sunrise;
                detailObj.sunset = respData.sunset;
                detailObj.city = respData.city_name;
                detailObj.time_zone = respData.timezone;
                detailObj.temp = respData.temp;

                WeatherbitLatLongResponse.Weather respWeatherDetail = respData.weather;
                detailObj.description = respWeatherDetail.description;

                return detailObj;
            } catch (Exception ex) {
                System.debug('exception caught at line #'+ex.getLineNumber());
                System.debug('exception message: '+ex.getMessage());
            }
        }
        return null;
    }
}

ParentComponent.html

<template>
    <div class="slds-m-bottom_small"><c-weatherbitheader></c-weatherbitheader></div>
    <lightning-card class="">
        <c-searchcomponent oncustevent={handleevent}></c-searchcomponent>
        <div if:true={parentresultdata}>
            <div class="slds-grid slds-wrap">
                <div class="slds-box slds-col slds-size_2-of-12 card slds-var-m-around_x-small" style="margin-left: 1.25rem; width: 15.5%;"><b> City:</b> {city}</div>
                <div class="slds-box slds-col slds-size_2-of-12 card slds-var-m-around_x-small" style="width: 15.5%;"><b> Sunrise(GMT):</b> <lightning-formatted-time value={sunrise}></lightning-formatted-time></div>
                <div class="slds-box slds-col slds-size_2-of-12 card slds-var-m-around_x-small" style="width: 15.5%;"><b> Sunset(GMT):</b> <lightning-formatted-time value={sunset}></lightning-formatted-time></div>
                <div class="slds-box slds-col slds-size_2-of-12 card slds-var-m-around_x-small" style="width: 15.5%;"><b> Temperature(in Celsius):</b> {temp}</div>
                <div class="slds-box slds-col slds-size_2-of-12 card slds-var-m-around_x-small" style="width: 15.5%;"><b> Weather:</b> {desc} </div>
                <div class="slds-box slds-col slds-size_2-of-12 card slds-var-m-around_x-small" style="width: 15.5%;"><b> Time Zone:</b> {timezone}</div>
            </div>
        </div>
        <div class="slds-var-m-around_large">
            <lightning-map
                map-markers={mapMarkers}
                zoom-level={zoomLevel}>
            </lightning-map>
        </div>
    </lightning-card>
</template>

ParentComponent.js

import { LightningElement, api, track } from 'lwc';
export default class Parentcomponent extends LightningElement {
    @api parentresultdata;
    @api mapMarkers = [];
    @track desc;
    @track sunrise;
    @track sunset;
    @track temp;
    @track timezone;
    @track city;
    zoomLevel = 10;
    @track classname;

    handleevent(event){
        this.parentresultdata = event.detail;
        if(this.parentresultdata){
            this.city = this.parentresultdata.city;
            this.sunrise = this.parentresultdata.sunrise;
            this.sunset = this.parentresultdata.sunset;
            this.temp = this.parentresultdata.temp;
            this.timezone = this.parentresultdata.time_zone;
            this.desc = this.parentresultdata.description;
            this.mapMarkers = [{
                    location: {
                        Latitude: this.parentresultdata.lat,
                        Longitude: this.parentresultdata.lon
                    }
            }];
        }
    }
}

Parentcomponent.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>48.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>
        <target>lightning__Tab</target>
    </targets>
</LightningComponentBundle>

Searchcomponent.html

<template>
    <div class="">
        <lightning-card>
            <lightning-layout>
                <lightning-layout-item size="4" medium-device-size="4" padding="around-small">
                    <div class="slds-box"> <span class="slds-text-heading_small">Search by City & Country</span>
                        <div class="slds-grid">
                            <div class="slds-col slds-size_1-of-2 slds-var-p-around_medium"><lightning-input onchange={handleCityChange} value={city} label="City"></lightning-input></div>
                            <div class="slds-col slds-size_1-of-2 slds-var-p-around_medium  "><lightning-input onchange={handleCountryChange} value={country} label="Country"></lightning-input></div>
                        </div>
                    </div>
                </lightning-layout-item>
                <lightning-layout-item size="4" medium-device-size="4" padding="around-small">
                    <div class="slds-box"> <span class="slds-text-heading_small">Search by Postal Code</span>
                        <div>
                            <div class="slds-grid">
                                <div class="slds-col slds-size_1-of-1 slds-var-p-around_medium"><lightning-input onchange={handlePostalCodeChange} type="counter" value={postalcode} label="Postal Code"></lightning-input></div>
                            </div>
                        </div>
                    </div>
                </lightning-layout-item>
                <lightning-layout-item size="4" medium-device-size="4" padding="around-small">
                    <div class="slds-box"> <span class="slds-text-heading_small">Search by Latitude & Longitude</span>
                        <div class="slds-grid">
                            <div class="slds-col slds-size_1-of-2 slds-var-p-around_medium"><lightning-input onchange={handleLatChange} type="number" value={latitude} step=".000001" label="Lat"></lightning-input></div>
                            <div class="slds-col slds-size_1-of-2 slds-var-p-around_medium  "><lightning-input onchange={handleLongChange} type="number" value={longitude} step=".000001" label="Long"></lightning-input></div>
                        </div>
                    </div>
                </lightning-layout-item></lightning-layout>
            <div class="slds-align_absolute-center">
                <lightning-button variant="brand" label="Search" onclick={handleSubmit} class="slds-m-left_x-small"></lightning-button>
                <lightning-button variant="destructive" label="Reset" onclick={handleReset} class="slds-m-left_x-small"></lightning-button>
            </div>
        </lightning-card>
    </div>
</template>

Searchcomponent.js

import { LightningElement, track, api } from 'lwc';
import getWeatherStatus from '@salesforce/apex/weatherbitcontroller.getWeatherStatus';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

export default class Searchcomponent extends LightningElement {
    @track latitude;
    @track longitude;
    @track result;
    @api dateresult;
    @track postalcode;
    @track city;
    @track country;
    handleLatChange(e){
        this.latitude = e.detail.value;
    }
    handleLongChange(e){
        this.longitude = e.detail.value;
    }
    handlePostalCodeChange(e){
        this.postalcode = e.detail.value;
    }
    handleCityChange(e){
        this.city = e.detail.value;
    }
    handleCountryChange(e){
        this.country = e.detail.value;
    }
    handleSubmit() {
        //resetting the value
        this.dateresult = '';
        const selectEvent = new CustomEvent('custevent', { detail: this.dateresult });
                    // Dispatches the event.
        console.log('event dispatching');
        this.dispatchEvent(selectEvent); 
        
        if (((this.postalcode) && (this.latitude)) ||((this.postalcode) && (this.longitude)) ) {
            this.showErrorToast('Please fill either of the inputs.');
            return;
        }
        else if (((this.postalcode) && (this.city)) ||((this.postalcode) && (this.country)) ) {
                this.showErrorToast('Please fill either of the inputs.');
                return;
            }
        else if (((this.latitude) && (this.city)) ||((this.latitude) && (this.city)) || ((this.longitude) && (this.city)) ||((this.longitude) && (this.city)) ) {
                this.showErrorToast('Please fill either of the inputs.');
                return;
            }
        else if( !(this.latitude || this.longitude || this.postalcode || this.city || this.country)){
            this.showErrorToast('Please complete the form and try again');
            return;
        }
        const allValid = [...this.template.querySelectorAll('lightning-input')]
            .reduce((validSoFar, inputCmp) => {
                        inputCmp.reportValidity();
                        return validSoFar && inputCmp.checkValidity();
            }, true);
        if (allValid) {
            
            getWeatherStatus({latitude : this.latitude, longitude: this.longitude, postalcode:this.postalcode, city: this.city, country:this.country})
            .then(result => {
                //this.accounts = result; 
                // Creates the custom event.
                if(result != null ){
                    this.dateresult = result;
                    const selectedEvent = new CustomEvent('custevent', { detail: this.dateresult });
                    // Dispatches the event.
                    console.log('event dispatching');
                    this.dispatchEvent(selectedEvent);
                }
                else{
                    this.showErrorToast('Some error occured. Please try again');
                }
                
            })
            .catch(error => {
                //this.error = error; 
            });
        }else {
            this.showErrorToast('Input Invalid. Please try again.');
        }
        
    }
    handleReset(){
        this.latitude   = '';
        this.longitude  = '';
        this.postalcode = '';
        this.city       = '';
        this.country    = ''
    }
    showErrorToast(msg) {
        const evt = new ShowToastEvent({
            title: 'Error mesage',
            message: msg,
            variant: 'error',
            mode: 'dismissable'
        });
        this.dispatchEvent(evt);
    }
}

Searchcomponent.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>48.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__Tab</target>
    </targets>
</LightningComponentBundle>

Weatherbitheader.html

<template>
    <div>
        <lightning-card  variant="Narrow"  title="Integrate Salesforce with WeatherBit.Io APIs" icon-name="utility:salesforce1">
        </lightning-card>
    </div>
</template>

Weatherbitheader.js

import { LightningElement } from 'lwc';

export default class Weatherbitheader extends LightningElement {}

Weatherbitheader.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>48.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>
        <target>lightning__Tab</target>
    </targets>
</LightningComponentBundle>

I hope you enjoy the article and learn from it. If you find some value in the article then please share it in your tech groups and with your friends.

Comment your query below. We would like to solve it. For learning together, you can join our group and subscribe us for the latest updates.

Follow us on twitter and join our WhatsApp group.

Here are the social media links of Rishabh. He will happy to help you.