Google OCR & Vision API Introduction & Authentication

0
578

Hi #Ohana,

Welcome back, it has been a while. In this blog post, we will talk about what Google OCR & Vision APIs are and how to get access token using the Salesforce VF page and apex class.

Introduction to Google Cloud Vision API

GC ( google cloud ) provides the free API which you can use for image labeling, face, logo, and landmark detection, optical character recognition (OCR), and detection of explicit content, into applications.

You can read the complete API documentation from Here

Authentication Overview

All the features of Google Cloud required a valid Access Token to make the request and then return the proper response. To get the access token google uses OAuth 2.O which is a 2 step process.

You can read the process of authentication with Google API from the official document of google cloud. Here is the link for the same.

Step 1 – Create custom metadata types – We will create 2 custom metadata types for authentication purpose.

1.1 – Google Config – The custom metadata types to store the configuration like authentication URL, token URL, client id, client secret &, etc. Below the image of the CM with all the fields

Below are the values for all the fields –

Field value
access type offline
authorize uri https://accounts.google.com/o/oauth2/v2/auth
grant_type authorization_code
response_type code
prompt consent
token URL https://www.googleapis.com/oauth2/v4/token
Content-Type application/x-www-form-urlencoded
model builtin/stable
client secret YOUR_CLIENT_SECRET
client id YOUR_CLIENT_ID
scope space separate scope values for example – https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/cloud-vision

Here is the list of all scope – link
redirect uri redirect URI is the return URL after authentication. you only need to put the VF page like apex/GoogleAuth
and you need to put the complete URL in google app like – https://back-to-basic-dev-ed–c.visualforce.com/apex/GoogleAUTH

Note: – You can get client Id, Client Secret from this page

1.2 – Google Token – This metadata is used to store the Google Access Token and Refresh Token. You only need to create the metadata and the record will be created by code after authentication.

Step 2 – Create Apex Class to create/update custom metadata

You can refer this link for the same

Step 3 – Create Apex Class “GoogleTokenUtility” – the class behind getting access token from Google and then creating the Custom Metadata Record.

Methods in Apex Class

Method Name Description
getInstance(String filter) A Reusable Method is used for getting the access token details from metadata Google Token
getConfig(String filter) A Reusable method to get the google configuration settings from the metadata Google Config
buildQueryAllString This is the reusable method for the SOQL query with all the fields.
getAuthCode The method used to get the auth code from google and then this auth code will be used to get access token
getAccessToken The method used to get the access token for authorized accounts.
doRefreshToken The method used for refreshing the token if expired
checkIfTokenIsValid Checks if the token is valid or not.
prepareRequest Used to prepare the HttpRequest

global without sharing class GoogleTokenUtility {
private static GoogleTokenUtility instance = null;
private static FINAL String QUERY_SELECT = 'SELECT ';
private static FINAL String QUERY_WHERE = ' WHERE ';
private static FINAL String QUERY_FROM = ' FROM ';
private static FINAL String QUERY_LIMIT = ' LIMIT ';
public static Google_Config__mdt googleConfig { get; set; }
public static Google_Token__mdt googleToken { get; set; }
public FINAL String baseURL = System.URL.getSalesforceBaseURL().toExternalForm()+'/';
public GoogleTokenUtility(){}
private GoogleTokenUtility(String filter){
getTokenInfo(filter);
}
public static GoogleTokenUtility getInstance(String filter){
if(instance == null) {
instance = new GoogleTokenUtility(filter);
}
return instance;
}
public static Google_Config__mdt getConfig(String filter){
if(GoogleTokenUtility.googleConfig == null) {
getConfigurationSettings(filter);
}
return GoogleTokenUtility.googleConfig;
}
private void getTokenInfo(String filter){
Schema.sObjectType sobj = Schema.getGlobalDescribe().get('Google_Token__mdt');
DescribeSObjectResult objectDescribe = sobj.getDescribe();
Map<String, Schema.SObjectField> fieldMap = objectDescribe.fields.getMap();
String whereClause = 'Masterlabel =:filter';
String query = buildQueryAllString(fieldMap.values(),objectDescribe, whereClause, 1);
List<Google_Token__mdt> tokenList = Database.Query(query);
if(tokenList != null && !tokenList.isEmpty()){
googleToken = tokenList.get(0);
}
}
private static void getConfigurationSettings(String filter){
Schema.sObjectType sobj = Schema.getGlobalDescribe().get('Google_Config__mdt');
DescribeSObjectResult objectDescribe = sobj.getDescribe();
Map<String, Schema.SObjectField> fieldMap = objectDescribe.fields.getMap();
String whereClause = 'Masterlabel =:filter';
String query = buildQueryAllString(fieldMap.values(),objectDescribe, whereClause, 1);
List<Google_Config__mdt> configList = Database.Query(query);
if(configList != null && !configList.isEmpty()){
googleConfig = configList.get(0);
}
}
private static String buildQueryAllString(List<Schema.SObjectField> allFields, DescribeSObjectResult obj, String filter, Integer rLimit) {
String query = QUERY_SELECT;
for(Schema.SObjectField dfr : allFields){
query = query + dfr.getDescribe().getName() + ',';
}
query = query.subString(0,query.length() - 1);
query = query + QUERY_FROM;
query = query + obj.getName();
if(!String.isBlank(filter)){
query = query + QUERY_WHERE;
query = query + filter;
}
query = query + QUERY_LIMIT;
query = query + rLimit;
return query;
}
public PageReference getAuthCode(){
getConfig('Google_Config'); // Note:- Use a custom label to store the value and then refer label here
if(GoogleTokenUtility.googleConfig == null){
// Error Here
return null;
}
String redirectURI = baseURL + GoogleTokenUtility.googleConfig.redirect_uri__c;
String authURL = GoogleTokenUtility.googleConfig.authorize_url__c+
'?client_id='+GoogleTokenUtility.googleConfig.client_id__c+'&'+
'response_type='+GoogleTokenUtility.googleConfig.response_type__c+'&'+
'scope='+GoogleTokenUtility.googleConfig.scope__c+'&'+
'access_type='+GoogleTokenUtility.googleConfig.access_type__c+'&'+
'prompt='+GoogleTokenUtility.googleConfig.prompt__c+'&'+
'redirect_uri='+redirectURI;
PageReference pageRef = new PageReference(authURL);
pageRef.setRedirect(true);
return pageRef;
}
public PageReference getAccessToken(){
String errorMessage = '';
String authCode = ApexPages.currentPage().getParameters().get('code');
getConfig('Google_Config'); // Note:- Use a custom label to store the value and then refer label here
if(GoogleTokenUtility.googleConfig == null || String.isBlank(authCode)){
// Error Here
return null;
}
String redirectURI = baseURL + GoogleTokenUtility.googleConfig.redirect_uri__c;
String requestBody = 'code='+authCode+'&'+
'client_id='+GoogleTokenUtility.googleConfig.client_id__c+'&'+
'client_secret='+GoogleTokenUtility.googleConfig.client_secret__c+'&'+
'redirect_uri='+redirectURI+'&'+
'grant_type='+GoogleTokenUtility.googleConfig.grant_type__c;
HttpRequest httpReq = prepareRequest(GoogleTokenUtility.googleConfig.Token_URL__c,'POST',requestBody,
GoogleTokenUtility.googleConfig.Content_Type__c);
HttpResponse httpRes = new HttpResponse();
try{
httpRes = (new Http()).send(httpReq);
if(httpRes.getStatusCode()==200){
Map<String, Object> responseMap = (Map<String, Object>)JSON.deserializeUntyped(httpRes.getBody());
Map<String, Object> tokenMap = new Map<String, Object>();
Integer seconds = (Integer)responseMap.get('expires_in');
tokenMap.put('access_token__c',(String)responseMap.get('access_token'));
tokenMap.put('expires_in__c',seconds);
tokenMap.put('expires_in_time__c',System.now().addSeconds(seconds));
tokenMap.put('External_Id__c','GoogleToken');
tokenMap.put('refresh_token__c',(String)responseMap.get('refresh_token'));
tokenMap.put('scope__c',(String)responseMap.get('scope'));
CreateUpdateMetadataUtils.createUpdateMetadata('Google_Token.GoogleToken','GoogleToken',tokenMap);
ApexPages.addmessage(new ApexPages.message(ApexPages.severity.INFO, 'You can close the Window now!'));
}else{
errorMessage = 'Unexpected Error while communicating with Google API. '
+'Status '+httpRes.getStatus()+' and Status Code '+httpRes.getStatuscode();
ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR, errorMessage));
}
}catch(System.CalloutException ex){
if(String.valueOf(ex).startsWith('Unauthorized endpoint')){
errorMessage = 'Unauthorize endpoint: An Administer must go to Setup -> Administer -> Security Control ->'
+' Remote Site Setting and add '+' '+ 'https://www.googleapis.com/' +' Endpoint';
ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR, errorMessage));
}
}catch(System.Exception ex){
if(String.valueOf(ex).startsWith('Unauthorized endpoint')){
errorMessage = 'Unauthorize endpoint: An Administer must go to Setup -> Administer -> Security Control ->'
+' Remote Site Setting and add '+' '+ 'https://www.googleapis.com/' +' Endpoint';
ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR, errorMessage));
}
}
return null;
}
public static Map<String, Object> doRefreshToken(Google_Token__mdt googleToken){
String errorMessage = '';
getConfig('Google_Config'); // Note:- Use a custom label to store the value and then refer label here
if(googleToken == null || GoogleTokenUtility.googleConfig == null){
// Error Here
return null;
}
String requestBody = 'client_id='+GoogleTokenUtility.googleConfig.client_id__c+'&'+
'client_secret='+GoogleTokenUtility.googleConfig.client_secret__c+'&'+
'refresh_token='+googleToken.refresh_token__c+'&'+
'grant_type=refresh_token';
HttpRequest httpReq = prepareRequest(GoogleTokenUtility.googleConfig.Token_URL__c,'POST',requestBody,
GoogleTokenUtility.googleConfig.Content_Type__c);
HttpResponse httpRes = new HttpResponse();
Map<String, Object> tokenMap = new Map<String, Object>();
try{
httpRes = (new Http()).send(httpReq);
if(httpRes.getStatusCode()==200){
Map<String, Object> responseMap = (Map<String, Object>)JSON.deserializeUntyped(httpRes.getBody());
Integer seconds = (Integer)responseMap.get('expires_in');
tokenMap.put('access_token__c',(String)responseMap.get('access_token'));
tokenMap.put('expires_in__c',seconds);
tokenMap.put('expires_in_time__c',System.now().addSeconds(seconds));
//CreateUpdateMetadataUtils.createUpdateMetadata('Google_Token.GoogleToken','GoogleToken',tokenMap);
}else{
errorMessage = 'Unexpected Error while communicating with Google API. '
+'Status '+httpRes.getStatus()+' and Status Code '+httpRes.getStatuscode();
}
}catch(System.CalloutException ex){
if(String.valueOf(ex).startsWith('Unauthorized endpoint')){
errorMessage = 'Unauthorize endpoint: An Administer must go to Setup -> Administer -> Security Control ->'
+' Remote Site Setting and add '+' '+ 'https://www.googleapis.com/' +' Endpoint';
}
}catch(System.Exception ex){
if(String.valueOf(ex).startsWith('Unauthorized endpoint')){
errorMessage = 'Unauthorize endpoint: An Administer must go to Setup -> Administer -> Security Control ->'
+' Remote Site Setting and add '+' '+ 'https://www.googleapis.com/' +' Endpoint';
}
}
return tokenMap;
}
public static Boolean checkIfTokenIsValid(Google_Token__mdt googleToken){
Boolean isExpired = false;
if(googleToken.expires_in_time__c < System.now()){
isExpired = true;
}
return isExpired;
}
public static HttpRequest prepareRequest(String endPoint, String Method, String Body, String contentType){
HttpRequest httpReq = new HttpRequest();
httpReq.setEndpoint(endPoint);
httpReq.setMethod(Method);
httpReq.setHeader('Content-Type',contentType);
if(!String.isBlank(body)){
httpReq.setBody(body);
}
return httpReq;
}
}

Step 4 – Create a VF Page “GoogleAUTH

<apex:page controller="GoogleTokenUtility"  lightningStylesheets="true">
  <apex:form >
      <apex:pageBlock title="Google Authentication">
          <apex:pageblockButtons >
              <apex:commandButton value="Authorize" action="{!getAuthCode}" />
          </apex:pageblockButtons>
      </apex:pageBlock>
  </apex:form>
</apex:page>

Step 5 – Create a new VF Page “CompleteGoogleAuth

<apex:page controller="GoogleTokenUtility" action="{!getAccessToken}" lightningStylesheets="true">
  <apex:form >
      <apex:pageBlock title="Google Authentication">
          <apex:pagemessages ></apex:pagemessages>
      </apex:pageBlock>
  </apex:form>
</apex:page>

Step 6 – Test the flow

Open the “GoogleAUTH” VF page and click on Preview. Now, Click on the Authorize button.

Thanks for reading 🙂

#Salesforce #Integration

 

LEAVE A REPLY

Please enter your comment!
Please enter your name here