Rendering GitHub JSON Data in Salesforce

In the past it was a huge challenge to provide system users with meaningful views across disparate data sets.  Data silos required complex integration and ETL (Extract-Transform-Load) tooling to provide correlations in a single user interface.  Lightning Connect makes this easy by allowing external data sources to be exposed in Salesforce and correlated with standard object, custom objects, and other external data sources.  This allows users to interact with data across multiple sources from a single user interface instead of having to jump between different systems.

Lightning Connect has out-of-the-box support for the OData format but also allows you to plug in custom adapters that can connect to any data source which Apex code can communicate with.  The Lightning Connect Custom Adapters enable you to easily pull in any RESTful (XML or JSON) data sources into Salesforce.  OAuth is also supported for services that use authentication.

Let’s walk through a simple example of using the Lightning Connect Custom Adapters to pull data from the GitHub API into Salesforce.  Custom Adapters are written in Apex and you can follow along using the Developer Console on any Salesforce org with Lightning Connect enabled.  Sign-up for a new Developer org for experimentation and testing if you need a clean slate to use.

The primary piece of a Custom Adapter is an Apex class that extends DataSource.Connection class and provides implementations for the sync, query, and search methods.  The sync method defines the table(s) and columns that Salesforce will understand.  A table has a name and columns.  Each column has a name and a data type.  A table is required to have at least an ExternalId and a DisplayUrl column.  This example will pull in the metadata for GitHub repositories so the table will also have the following columns:

  • Name – The repository’s name
  • Description – The repository’s description
  • Stars – Number of “stars” the repository has received

The query method is what is used to actually fetch data from the external data source and populate the rows.  The search method is optional and won’t be used in this example however this could be connected to the GitHub search API.

Here is a GitHubConnection Apex class that implements the sync and query methods:

global class GitHubConnection extends DataSource.Connection {
    
    override global List<DataSource.Table> sync() {
        List<DataSource.Column> gitHubRepoColumns = new List<DataSource.Column>();
        gitHubRepoColumns.add(DataSource.Column.text('ExternalId', 255));
        gitHubRepoColumns.add(DataSource.Column.url('DisplayUrl'));
        gitHubRepoColumns.add(DataSource.Column.text('Name', 128));
        gitHubRepoColumns.add(DataSource.Column.text('Description', 1024));
        gitHubRepoColumns.add(DataSource.Column.number('Stars', 16, 0));
        
        List<DataSource.Table> tables = new List<DataSource.Table>();
        tables.add(DataSource.Table.get('GitHub Repos', 'Name', gitHubRepoColumns));
        return tables;
    }
    
    override global DataSource.TableResult query(DataSource.QueryContext queryContext) {
        List<Map<String, Object>> repos = DataSource.QueryUtils.process(queryContext, getOrgRepos('developerforce'));
        DataSource.TableResult tableResult = DataSource.TableResult.get(queryContext, repos);
        return tableResult;
    }
    
    override global List<DataSource.TableResult> search(DataSource.SearchContext searchContext) {
        return DataSource.SearchUtils.searchByName(searchContext, this);
    }
    
    public List<Map<String, Object>> getOrgRepos(String org) {
        Http httpProtocol = new Http();
        HttpRequest request = new HttpRequest();
        String url = 'https://api.github.com/orgs/' + org + '/repos?per_page=1000';
        request.setEndPoint(url);
        request.setMethod('GET');
        HttpResponse response = httpProtocol.send(request);
        
        List<Map<String, Object>> repos = new List<Map<String, Object>>();
        
        for (Object item : (List<Object>)JSON.deserializeUntyped(response.getBody())) {
            Map<String, Object> repo = (Map<String, Object>)item;
            repo.put('ExternalId', repo.get('id'));
            repo.put('DisplayUrl', repo.get('html_url'));
            repo.put('Name', repo.get('name'));
            repo.put('Description', repo.get('description'));
            repo.put('Stars', repo.get('stargazers_count'));
            repos.add(repo);
        }
        
        return repos;
    }
    
}

The sync method just creates a single table with the columns described above.  The query method calls the getOrgRepos(String org) method where the real work of communicating with the GitHub API happens.  That method uses the Http class to make a request to GitHub’s JSON API, fetching the list of repos in the specified organization.  Notice that the developerforce organization is specified in the query method.  The response is then parsed as JSON data and then the fields are mapped to the correct column using a Map<String, Object> for each repository.  The getOrgRepos method was separated from query so that it can easily be tested.  You can find the test code in the developerforce/github-custom-adapter GitHub project.

To expose the GitHubConnection so that it can be used as a Custom Adapter we need a class that extends DataSource.Provider, describes the capabilities the adapter provides and the connection class.  Here is the GitHubProvider class:

global class GitHubProvider extends DataSource.Provider {

    override global List<DataSource.Capability> getCapabilities() {
    	List<DataSource.Capability> capabilities = new List<DataSource.Capability>();
    	capabilities.add(DataSource.Capability.ROW_QUERY);
        return capabilities;
    }
    
    override global List<DataSource.AuthenticationCapability> getAuthenticationCapabilities() {
        List<DataSource.AuthenticationCapability> capabilities = new List<DataSource.AuthenticationCapability>();
        capabilities.add(DataSource.AuthenticationCapability.ANONYMOUS);
        return capabilities;
    }
    
    override global DataSource.Connection getConnection(DataSource.ConnectionParams connectionsParams) {
        return new GitHubConnection();
    }
    
}

The getCapabilities method in this case just says that the GitHub Adapter provides a queryable service.  A number of other capabilities like searchability can also be specified.  The getAuthenticationCapabilities method describes how to authenticate to the service.  In this case GitHub provides anonymous, rate-limited access so that is what is specified.  The getConnection method simply returns an instance of the GitHubConnection class.  That is all that is needed to create the custom adapter!  But to enable the Salesforce org to make the HTTP call to the GitHub API you must create a new Remote Site Setting:

Now you can create the External Data Source using your GitHub Custom Adapter:

Then click “Validate and Sync”, select the “GitHub Repos” table and select “Sync” to complete the setup:

You can now create a new Custom Object Tab for the “GitHub Repos” object:

Navigating to that tab and selecting to view all of the rows should show you a list of the GitHub repos in the “developerforce” organization:

When you view this list in Salesforce the GitHubConnection.query method is being used to fetch the data from GitHub’s JSON API.  Selecting an External Id will take you to the details for the record and selecting the Display URL will take you to the repo’s page on GitHub.

In addition to the standard Salesforce web and mobile user interfaces, the GitHub Repo data is also available via the Salesforce REST APIs and from Apex via standard SOQL queries.  For example, in the Developer Console’s Query Editor you can run the following SOQL query to get a list of the repos and their number of stars:

SELECT Name__c, Stars__c FROM GitHub_Repos__x

In this simple example the data requests are anonymous but you could easily add OAuth support to the GitHub Custom Adapter in order to provide authenticated access.  Check the docs for more info about adding authentication support to custom adapters.

As you’ve seen it is now incredibly easy to bring just about any external data source into Salesforce using Lightning Connect Custom Adapters.  And we’ve just scratched the surface of the data mapping and correlation features.  If you wanted to associate each GitHub repo to an Account you can easily do that using the lookup column data type.  Then Salesforce users could browse from an Account to their associated GitHub repos all without leaving the standard Salesforce user interface.

To continue learning about Lightning Connect Custom Adapters, check out the documentation and let us know how it goes!

Leave your comments...

Rendering GitHub JSON Data in Salesforce