For this example to work reliably, request offline access when setting up OAuth so that Salesforce can obtain and maintain a refresh token for your connections.
/** * Extends the DataSource.Connection class to enable * Salesforce to sync the external system’s schema * and to handle queries and searches of the external data. **/ global class DriveDataSourceConnection extends DataSource.Connection { private DataSource.ConnectionParams connectionInfo; /** * Constructor for DriveDataSourceConnection **/ global DriveDataSourceConnection( DataSource.ConnectionParams connectionInfo) { this.connectionInfo = connectionInfo; } /** * Called when an external object needs to get a list of * schema from the external data source, for example when * the administrator clicks “Validate and Sync” in the * user interface for the external data source. **/ override global List<DataSource.Table> sync() { List<DataSource.Table> tables = new List<DataSource.Table>(); List<DataSource.Column> columns; columns = new List<DataSource.Column>(); columns.add(DataSource.Column.text('title', 255)); columns.add(DataSource.Column.text('description',255)); columns.add(DataSource.Column.text('createdDate',255)); columns.add(DataSource.Column.text('modifiedDate',255)); columns.add(DataSource.Column.url('selfLink')); columns.add(DataSource.Column.url('DisplayUrl')); columns.add(DataSource.Column.text('ExternalId',255)); tables.add(DataSource.Table.get('googleDrive','title', columns)); return tables; } /** * Called to query and get results from the external * system for SOQL queries, list views, and detail pages * for an external object that’s associated with the * external data source. * * The QueryContext argument represents the query to run * against a table in the external system. * * Returns a list of rows as the query results. **/ override global DataSource.TableResult query( DataSource.QueryContext context) { DataSource.Filter filter = context.tableSelection.filter; String url; if (filter != null) { String thisColumnName = filter.columnName; if (thisColumnName != null && thisColumnName.equals('ExternalId')) url = 'https://www.googleapis.com/drive/v2/' + 'files/' + filter.columnValue; else url = 'https://www.googleapis.com/drive/v2/' + 'files'; } else { url = 'https://www.googleapis.com/drive/v2/' + 'files'; } /** * Filters, sorts, and applies limit and offset clauses. **/ List<Map<String, Object>> rows = DataSource.QueryUtils.process(context, getData(url)); return DataSource.TableResult.get(true, null, context.tableSelection.tableSelected, rows); } /** * Called to do a full text search and get results from * the external system for SOSL queries and Salesforce * global searches. * * The SearchContext argument represents the query to run * against a table in the external system. * * Returns results for each table that the SearchContext * requested to be searched. **/ override global List<DataSource.TableResult> search( DataSource.SearchContext context) { List<DataSource.TableResult> results = new List<DataSource.TableResult>(); for (Integer i =0;i< context.tableSelections.size();i++) { String entity = context.tableSelections[i].tableSelected; String url = 'https://www.googleapis.com/drive/v2/files'+ '?q=fullText+contains+\''+context.searchPhrase+'\''; results.add(DataSource.TableResult.get( true, null, entity, getData(url))); } return results; } /** * Helper method to parse the data. * The url argument is the URL of the external system. * Returns a list of rows from the external system. **/ public List<Map<String, Object>> getData(String url) { HttpResponse response = getResponse(url); List<Map<String, Object>> rows = new List<Map<String, Object>>(); Map<String, Object> responseBodyMap = (Map<String, Object>) JSON.deserializeUntyped(response.getBody()); /** * Checks errors. **/ Map<String, Object> error = (Map<String, Object>)responseBodyMap.get('error'); if (error!=null) { List<Object> errorsList = (List<Object>)error.get('errors'); Map<String, Object> errors = (Map<String, Object>)errorsList[0]; String errorMessage = (String)errors.get('message'); throw new DataSource.OAuthTokenExpiredException(errorMessage); } List<Object> fileItems=(List<Object>)responseBodyMap.get('items'); if (fileItems != null) { for (Integer i=0; i < fileItems.size(); i++) { Map<String, Object> item = (Map<String, Object>)fileItems[i]; rows.add(createRow(item)); } } else { rows.add(createRow(responseBodyMap)); } return rows; } /** * Helper method to populate the External ID and Display * URL fields on external object records based on the 'id' * value that’s sent by the external system. * * The Map<String, Object> item parameter maps to the data * that represents a row. * * Returns an updated map with the External ID and * Display URL values. **/ public Map<String, Object> createRow( Map<String, Object> item){ Map<String, Object> row = new Map<String, Object>(); for ( String key : item.keySet() ) { if (key == 'id') { row.put('ExternalId', item.get(key)); } else if (key=='selfLink') { row.put(key, item.get(key)); row.put('DisplayUrl', item.get(key)); } else { row.put(key, item.get(key)); } } return row; } /** * Helper method to make the HTTP GET call. * The url argument is the URL of the external system. * Returns the response from the external system. **/ public HttpResponse getResponse(String url) { Http httpProtocol = new Http(); HttpRequest request = new HttpRequest(); request.setEndPoint(url); request.setMethod('GET'); request.setHeader('Authorization', 'Bearer '+ this.connectionInfo.oauthToken); HttpResponse response = httpProtocol.send(request); return response; } }
/** * Extends the DataSource.Provider base class to create a * custom adapter for Lightning Connect. The class informs * Salesforce of the functional and authentication * capabilities that are supported by or required to connect * to an external system. **/ global class DriveDataSourceProvider extends DataSource.Provider { /** * Declares the types of authentication that can be used * to access the external system **/ override global List<DataSource.AuthenticationCapability> getAuthenticationCapabilities() { List<DataSource.AuthenticationCapability> capabilities = new List<DataSource.AuthenticationCapability>(); capabilities.add( DataSource.AuthenticationCapability.OAUTH); capabilities.add( DataSource.AuthenticationCapability.ANONYMOUS); return capabilities; } /** * Declares the functional capabilities that the * external system supports. **/ override global List<DataSource.Capability> getCapabilities() { List<DataSource.Capability> capabilities = new List<DataSource.Capability>(); capabilities.add(DataSource.Capability.ROW_QUERY); capabilities.add(DataSource.Capability.SEARCH); return capabilities; } /** * Declares the associated DataSource.Connection class. **/ override global DataSource.Connection getConnection( DataSource.ConnectionParams connectionParams) { return new DriveDataSourceConnection(connectionParams); } }