Integrate Salesforce Org with Einstein Image Classification API
Prerequisites
Set up your Account-- Navigate to Einstein Platform Service Account .
Find your key file-- If you have already created an account, locate the einstein_platform.pem
Find your key file-- If you have already created an account, locate the einstein_platform.pem
file that you have downloaded.
Upload Your File
Step 1: Log in to salesforce
Step 2:Click Files
Step 3:Click Upload Files
Step 4:Navigate to the directory where you saved the einstein_platform.pem file , select the file , and click Open.
Create VisualForce Page
VisualForce Page Name -- Predict
Upload Your File
Step 1: Log in to salesforce
Step 2:Click Files
Step 3:Click Upload Files
Step 4:Navigate to the directory where you saved the einstein_platform.pem file , select the file , and click Open.
Create a Remote Site
Step 1:Log in to salesforce
Step 2:From SetUp Enter Remote Site in Quick Find box, select Remote Site Settings.
Step 3:
- Enter a name for the Remote Site.
- In the Remote Site URL field , enter https://api.metamind.io .
Step 4: Click Save .
Create The Apex Classes
Class Name -- JWT
public class JWT {
public String alg {get;set;}
public String iss {get;set;}
public String sub {get;set;}
public String aud {get;set;}
public String exp {get;set;}
public String iat {get;set;}
public Map<String,String> claims {get;set;}
public Integer validFor {get;set;}
public String cert {get;set;}
public String pkcs8 {get;set;}
public String privateKey {get;set;}
public static final String HS256 = 'HS256';
public static final String RS256 = 'RS256';
public static final String NONE = 'none';
public JWT(String alg) {
this.alg = alg;
this.validFor = 300;
}
public String issue() {
String jwt = '';
JSONGenerator header = JSON.createGenerator(false);
header.writeStartObject();
header.writeStringField('alg', this.alg);
header.writeEndObject();
String encodedHeader = base64URLencode(Blob.valueOf(header.getAsString()));
JSONGenerator body = JSON.createGenerator(false);
body.writeStartObject();
body.writeStringField('iss', this.iss);
body.writeStringField('sub', this.sub);
body.writeStringField('aud', this.aud);
Long rightNow = (dateTime.now().getTime()/1000)+1;
body.writeNumberField('iat', rightNow);
body.writeNumberField('exp', (rightNow + validFor));
if (claims != null) {
for (String claim : claims.keySet()) {
body.writeStringField(claim, claims.get(claim));
}
}
body.writeEndObject();
jwt = encodedHeader + '.' + base64URLencode(Blob.valueOf(body.getAsString()));
if ( this.alg == HS256 ) {
Blob key = EncodingUtil.base64Decode(privateKey);
Blob signature = Crypto.generateMac('hmacSHA256',Blob.valueof(jwt),key);
jwt += '.' + base64URLencode(signature);
} else if ( this.alg == RS256 ) {
Blob signature = null;
if (cert != null ) {
signature = Crypto.signWithCertificate('rsa-sha256', Blob.valueOf(jwt), cert);
} else {
Blob privateKey = EncodingUtil.base64Decode(pkcs8);
signature = Crypto.sign('rsa-sha256', Blob.valueOf(jwt), privateKey);
}
jwt += '.' + base64URLencode(signature);
} else if ( this.alg == NONE ) {
jwt += '.';
}
return jwt;
}
public String base64URLencode(Blob input){
String output = encodingUtil.base64Encode(input);
output = output.replace('+', '-');
output = output.replace('/', '_');
while ( output.endsWith('=')){
output = output.subString(0,output.length()-1);
}
return output;
}
}
Class Name -- JWTBearerFlow
public class JWTBearerFlow {
public static String getAccessToken(String tokenEndpoint, JWT jwt) {
String access_token = null;
String body = 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=' + jwt.issue();
HttpRequest req = new HttpRequest();
req.setMethod('POST');
req.setEndpoint(tokenEndpoint);
req.setHeader('Content-type', 'application/x-www-form-urlencoded');
req.setBody(body);
Http http = new Http();
HTTPResponse res = http.send(req);
if ( res.getStatusCode() == 200 ) {
System.JSONParser parser = System.JSON.createParser(res.getBody());
while (parser.nextToken() != null) {
if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && (parser.getText() == 'access_token')) {
parser.nextToken();
access_token = parser.getText();
break;
}
}
}
return access_token;
}
}
Class Name -- HttpFormBuilder
public class HttpFormBuilder {
// The boundary is alligned so it doesn't produce padding characters when base64 encoded.
private final static string Boundary = '1ff13444ed8140c7a32fc4e6451aa76d';
public static string GetContentType() {
return 'multipart/form-data; charset="UTF-8"; boundary="' + Boundary + '"';
}
private static string SafelyPad(
string value,
string valueCrLf64,
string lineBreaks) {
string valueCrLf = '';
blob valueCrLfBlob = null;
while (valueCrLf64.endsWith('=')) {
value += ' ';
valueCrLf = value + lineBreaks;
valueCrLfBlob = blob.valueOf(valueCrLf);
valueCrLf64 = EncodingUtil.base64Encode(valueCrLfBlob);
}
return valueCrLf64;
}
public static string WriteBoundary() {
string value = '--' + Boundary + '\r\n';
blob valueBlob = blob.valueOf(value);
return EncodingUtil.base64Encode(valueBlob);
}
public static string WriteBoundary(
EndingType ending) {
string value = '';
if (ending == EndingType.Cr) {
value += '\n';
} else if (ending == EndingType.None) {
value += '\r\n';
}
value += '--' + Boundary + '--';
blob valueBlob = blob.valueOf(value);
return EncodingUtil.base64Encode(valueBlob);
}
public static string WriteBodyParameter(
string key,
string value) {
string contentDisposition = 'Content-Disposition: form-data; name="' + key + '"';
string contentDispositionCrLf = contentDisposition + '\r\n\r\n';
blob contentDispositionCrLfBlob = blob.valueOf(contentDispositionCrLf);
string contentDispositionCrLf64 = EncodingUtil.base64Encode(contentDispositionCrLfBlob);
string content = SafelyPad(contentDisposition, contentDispositionCrLf64, '\r\n\r\n');
string valueCrLf = value + '\r\n';
blob valueCrLfBlob = blob.valueOf(valueCrLf);
string valueCrLf64 = EncodingUtil.base64Encode(valueCrLfBlob);
content += SafelyPad(value, valueCrLf64, '\r\n');
return content;
}
public enum EndingType {
Cr,
CrLf,
None
}
}
Class Name--Vision
public class Vision {
public static String VISION_API = 'https://api.metamind.io/v1/vision';
public static String PREDICT = VISION_API + '/predict';
public static List<Prediction> predictUrl(String url, String access_token, String model) {
return predictInternal(url, access_token, model, false);
}
public static List<Prediction> predictBase64(String base64String, String access_token, String model) {
return predictInternal(base64String, access_token, model, true);
}
public static List<Prediction> predictBlob(blob fileBlob, String access_token, String model) {
return predictInternal(EncodingUtil.base64Encode(fileBlob), access_token, model, true);
}
private static List<Prediction> predictInternal(String sample, String access_token, String model, boolean isBase64) {
string contentType = HttpFormBuilder.GetContentType();
// Compose the form
string form64 = '';
form64 += HttpFormBuilder.WriteBoundary();
form64 += HttpFormBuilder.WriteBodyParameter('modelId', EncodingUtil.urlEncode(model, 'UTF-8'));
form64 += HttpFormBuilder.WriteBoundary();
if(isBase64) {
form64 += HttpFormBuilder.WriteBodyParameter('sampleBase64Content', sample);
} else {
form64 += HttpFormBuilder.WriteBodyParameter('sampleLocation', sample);
}
form64 += HttpFormBuilder.WriteBoundary(HttpFormBuilder.EndingType.CrLf);
blob formBlob = EncodingUtil.base64Decode(form64);
string contentLength = string.valueOf(formBlob.size());
// Compose the http request
HttpRequest httpRequest = new HttpRequest();
httpRequest.setBodyAsBlob(formBlob);
httpRequest.setHeader('Connection', 'keep-alive');
httpRequest.setHeader('Content-Length', contentLength);
httpRequest.setHeader('Content-Type', contentType);
httpRequest.setMethod('POST');
httpRequest.setTimeout(120000);
httpRequest.setHeader('Authorization','Bearer ' + access_token);
httpRequest.setEndpoint(PREDICT);
Http http = new Http();
List<Prediction> predictions = new List<Prediction>();
try {
HTTPResponse res = http.send(httpRequest);
if (res.getStatusCode() == 200) {
System.JSONParser parser = System.JSON.createParser(res.getBody());
while (parser.nextToken() != null) {
if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && (parser.getText() == 'probabilities')) {
parser.nextToken();
if (parser.getCurrentToken() == JSONToken.START_ARRAY) {
while (parser.nextToken() != null) {
// Advance to the start object marker to
// find next probability object.
if (parser.getCurrentToken() == JSONToken.START_OBJECT) {
// Read entire probability object
Prediction probability = (Prediction)parser.readValueAs(Vision.Prediction.class);
predictions.add(probability);
}
}
}
break;
}
}
}
//System.debug(res.toString());
//System.debug('STATUS:'+res.getStatus());
//System.debug('STATUS_CODE:'+res.getStatusCode());
} catch(System.CalloutException e) {
System.debug('ERROR:' + e);
}
return(predictions);
}
public class Prediction {
public String label {get;set;}
public Double probability {get;set;}
}
}
Class Name -- VisionController
public class VisionController {
// You can upload the `einstein_platform.pem` into your Salesforce org as `File` sObject and read it as below
public String getAccessToken() {
// Ignore the File upload part and "jwt.pkcs" if you used a Salesforce certificate to sign up
// for an Einstein Platform account
ContentVersion base64Content = [SELECT Title, VersionData FROM ContentVersion where Title='einstein_platform' OR Title='predictive_services' ORDER BY Title LIMIT 1];
String keyContents = base64Content.VersionData.tostring();
keyContents = keyContents.replace('-----BEGIN RSA PRIVATE KEY-----', '');
keyContents = keyContents.replace('-----END RSA PRIVATE KEY-----', '');
keyContents = keyContents.replace('\n', '');
// Get a new token
JWT jwt = new JWT('RS256');
// jwt.cert = 'JWTCert'; // Uncomment this if you used a Salesforce certificate to sign up for an Einstein Platform account
jwt.pkcs8 = keyContents; // Comment this if you are using jwt.cert
jwt.iss = 'developer.force.com';
jwt.sub = 'sireesharatnam9@gmail.com';
jwt.aud = 'https://api.metamind.io/v1/oauth2/token';
jwt.exp = '3600';
String access_token = JWTBearerFlow.getAccessToken('https://api.metamind.io/v1/oauth2/token', jwt);
return access_token;
}
public List<Vision.Prediction> getCallVisionUrl() {
// Get a new token
String access_token = getAccessToken();
// Make a prediction using URL to a file
return Vision.predictUrl('https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSkE0RljI_f3ssEs06QjwVsh_NmcQuCEd6E4ItPbwWH-DkhA1oIyg',access_token,'GeneralImageClassifier');
}
public List<Vision.Prediction> getCallVisionContent() {
// Get a new token
String access_token = getAccessToken();
// Make a prediction for an image stored in Salesforce
// by passing the file as blob which is then converted to base64 string
ContentVersion content = [SELECT Title,VersionData FROM ContentVersion where Id = '06841000000LkfCAAS' LIMIT 1];
return Vision.predictBlob(content.VersionData, access_token, 'GeneralImageClassifier');
}
}
VisualForce Page Name -- Predict
<apex:page Controller="VisionController"> <apex:form > <apex:pageBlock > <apex:image url="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSkE0RljI_f3ssEs06QjwVsh_NmcQuCEd6E4ItPbwWH-DkhA1oIyg"> </apex:image> <br/> <apex:repeat value="{!AccessToken}" var="accessToken"> Access Token:<apex:outputText value="{!accessToken}" /><br/> </apex:repeat> <br/> <apex:repeat value="{!callVisionUrl}" var="prediction"> <apex:outputText value="{!prediction.label}" />:<apex:outputText value="{!prediction.probability}" /><br/> </apex:repeat> </apex:pageBlock> </apex:form> </apex:page>
Click Preview
Comments
Post a Comment