Skip to content

Commit

Permalink
Merge pull request #1217 from ashitsalesforce/master
Browse files Browse the repository at this point in the history
refactoring and subclassing for clarity
  • Loading branch information
ashitsalesforce authored Aug 12, 2024
2 parents 0c4f487 + 9f6fcdc commit 7ec745a
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 242 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,203 @@
*/
package com.salesforce.dataloader.action.visitor.rest;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.salesforce.dataloader.client.HttpClientTransport;
import com.salesforce.dataloader.client.HttpTransportInterface;
import com.salesforce.dataloader.client.SessionInfo;
import com.salesforce.dataloader.client.CompositeRESTClient.ACTION_ENUM;
import com.salesforce.dataloader.config.Config;
import com.salesforce.dataloader.config.Messages;
import com.salesforce.dataloader.controller.Controller;
import com.salesforce.dataloader.dyna.SforceDynaBean;
import com.salesforce.dataloader.exception.ParameterLoadException;
import com.salesforce.dataloader.util.AppUtil;
import com.sforce.async.AsyncApiException;
import com.sforce.soap.partner.SaveResult;
import com.sforce.soap.partner.StatusCode;
import com.sforce.soap.partner.fault.ApiFault;
import com.sforce.ws.ConnectionException;
import com.sforce.ws.ConnectorConfig;

public class RESTConnection {
private ConnectorConfig config;
private ConnectorConfig connectorConfig;
private Controller controller;
private static Logger logger = LogManager.getLogger(RESTConnection.class);

public RESTConnection(ConnectorConfig config, Controller controller) throws AsyncApiException {
this.config = config;
this.connectorConfig = config;
this.controller = controller;
}


@SuppressWarnings("unchecked")
public SaveResult[] loadAction(SessionInfo session, ACTION_ENUM action, List<DynaBean> dynabeans) throws ConnectionException {
String actionStr = "update"; // default
switch (action) {
case DELETE:
actionStr = "delete";
break;
default:
actionStr = "update";
break;
}
logger.debug(Messages.getFormattedString("Client.beginOperation", actionStr)); //$NON-NLS-1$
ConnectionException connectionException = null;
try {
Map<String, Object> batchRecords = this.getSobjectMapForCompositeREST(dynabeans, "update");
String json = "";
try {
json = AppUtil.serializeToJson(batchRecords);
logger.debug("JSON for batch update using Composite REST:\n" + json);
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
logger.error(e.getMessage());
throw new ConnectionException(e.getMessage());
}

HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/JSON");
headers.put("ACCEPT", "application/JSON");
headers.put("Authorization", "Bearer " + session.getSessionId());
String lookupFieldName = Config.getCurrentConfig().getString(Config.IDLOOKUP_FIELD);
if (lookupFieldName == null || lookupFieldName.isBlank()) {
lookupFieldName = "id";
}
if (!"id".equalsIgnoreCase(lookupFieldName)
&& Controller.getAPIMajorVersion() < 61) {
String message = "update operation does not support referencing objects using a non-id field such as "
+ lookupFieldName + " for API version 60 or lower. The current API version is "
+ Controller.getAPIVersion();
logger.error(message);
throw new ConnectionException(message);
}
HttpClientTransport transport = new HttpClientTransport(this.connectorConfig);

// assume update operation by default and set http method value to PATCH
HttpTransportInterface.SupportedHttpMethodType httpMethod = HttpTransportInterface.SupportedHttpMethodType.PATCH;
if (action == ACTION_ENUM.DELETE) {
httpMethod = HttpTransportInterface.SupportedHttpMethodType.DELETE;
}
try {
OutputStream out = transport.connect(
connectorConfig.getRestEndpoint()
+ controller.getConfig().getString(Config.ENTITY)
+ "/" + lookupFieldName + "/"
+ "?updateOnly=true",
headers,
true,
httpMethod);
out.write(json.getBytes(StandardCharsets.UTF_8.name()));
out.close();
} catch (IOException e) {
logger.error(e.getMessage());
throw new ConnectionException(e.getMessage());
}
InputStream in = null;
try {
in = transport.getContent();
} catch (IOException e) {
logger.error(e.getMessage());
throw new ConnectionException(e.getMessage());
}
boolean successfulRequest = transport.isSuccessful();
ArrayList<SaveResult> resultList = new ArrayList<SaveResult>();
if (successfulRequest) {
Object[] jsonResults = null;
try {
jsonResults = AppUtil.deserializeJsonToObject(in, Object[].class);
} catch (IOException e) {
logger.warn("Composite REST returned no results - " + e.getMessage());
throw new ConnectionException(e.getMessage());
}
for (Object result : jsonResults) {
Map<String, Object> resultMap = (Map<String, Object>)result;
SaveResult resultToSave = new SaveResult();
resultToSave.setId((String)resultMap.get("id"));
resultToSave.setSuccess(((Boolean)resultMap.get("success")).booleanValue());
List<Map<String, Object>> errorResultsArray = (List<Map<String, Object>>)resultMap.get("errors");
ArrayList<com.sforce.soap.partner.Error> errorList = new ArrayList<com.sforce.soap.partner.Error>();
if (errorResultsArray != null) {
for (Map<String, Object> errorMap : errorResultsArray) {
com.sforce.soap.partner.Error error = new com.sforce.soap.partner.Error();
String codeStr = StatusCode.valuesToEnums.get((String)errorMap.get("statusCode"));
StatusCode statusCode = StatusCode.valueOf(codeStr);
error.setStatusCode(statusCode);
error.setMessage((String) errorMap.get("message"));
List<String> fieldsList = (List<String>) errorMap.get("fields");
error.setFields(fieldsList.toArray(new String[1]));
errorList.add(error);
}
resultToSave.setErrors(errorList.toArray(new com.sforce.soap.partner.Error[1]));
}
resultList.add(resultToSave);
}
} else {
try {
String resultStr = IOUtils.toString(in, StandardCharsets.UTF_8);
logger.warn(resultStr);
} catch (IOException e) {
logger.warn(e.getMessage());
}
}
session.performedSessionActivity(); // reset session activity timer
return resultList.toArray(new SaveResult[0]);
} catch (ConnectionException ex) {
logger.error(
Messages.getFormattedString(
"Client.operationError", new String[]{actionStr, ex.getMessage()}), ex); //$NON-NLS-1$
if (ex instanceof ApiFault) {
ApiFault fault = (ApiFault)ex;
String faultMessage = fault.getExceptionMessage();
logger.error(
Messages.getFormattedString(
"Client.operationError", new String[]{actionStr, faultMessage}), fault); //$NON-NLS-1$
}
connectionException = ex;
}
throw connectionException;
}

private Map<String, Object> getSobjectMapForCompositeREST(List<DynaBean> dynaBeans, String opName) {
try {
List<Map<String, Object>> sobjectList = SforceDynaBean.getRESTSObjectArray(controller, dynaBeans, controller.getConfig().getString(Config.ENTITY),
controller.getConfig().getBoolean(Config.INSERT_NULLS));
HashMap<String, Object> recordsMap = new HashMap<String, Object>();
recordsMap.put("records", sobjectList);
recordsMap.put("allOrNone", false);
return recordsMap;
} catch (IllegalAccessException ex) {
logger.error(
Messages.getFormattedString("Client.operationError", new String[]{opName, ex.getMessage()}), ex); //$NON-NLS-1$
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
logger.error(
Messages.getFormattedString("Client.operationError", new String[]{opName, ex.getMessage()}), ex); //$NON-NLS-1$
throw new RuntimeException(ex);
} catch (NoSuchMethodException ex) {
logger.error(
Messages.getFormattedString("Client.operationError", new String[]{opName, ex.getMessage()}), ex); //$NON-NLS-1$
throw new RuntimeException(ex);
} catch (ParameterLoadException ex) {
logger.error(
Messages.getFormattedString("Client.operationError", new String[]{opName, ex.getMessage()}), ex); //$NON-NLS-1$
throw new RuntimeException(ex);
}
}

}
27 changes: 7 additions & 20 deletions src/main/java/com/salesforce/dataloader/client/BulkV1Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,18 @@
* @author Colin Jarvis
* @since 17.0
*/
public class BulkV1Client extends ClientBase<BulkV1Connection> {
public class BulkV1Client extends RESTClient<BulkV1Connection> {
private static Logger LOG = LogManager.getLogger(BulkV1Client.class);
private BulkV1Connection connection;
private ConnectorConfig connectorConfig = null;

public BulkV1Client(Controller controller) {
super(controller, LOG);
}

@Override
public BulkV1Connection getConnection() {
return connection;
}

@Override
protected boolean connectPostLogin(ConnectorConfig cc) {
try {
// Set up a connection object with the given config
this.connection = new BulkV1Connection(cc);
setConnection(new BulkV1Connection(cc));
} catch (AsyncApiException e) {
logger.error(Messages.getMessage(getClass(), "loginError", cc.getAuthEndpoint(), e.getExceptionMessage()),
e);
Expand All @@ -69,19 +62,13 @@ protected boolean connectPostLogin(ConnectorConfig cc) {
return true;
}

@Override
public synchronized ConnectorConfig getConnectorConfig() {
this.connectorConfig = super.getConnectorConfig();
// override the restEndpoint value set in the superclass
String server = getSession().getServer();
if (server != null) {
this.connectorConfig.setRestEndpoint(server + getServicePath());
}
return this.connectorConfig;
}

public static String getServicePath() {
return "/services/async/" + getAPIVersionForTheSession() + "/";
}

@Override
public String getServiceURLPath() {
return getServicePath();
}

}
28 changes: 8 additions & 20 deletions src/main/java/com/salesforce/dataloader/client/BulkV2Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,18 @@
import com.sforce.async.AsyncApiException;
import com.sforce.ws.ConnectorConfig;

public class BulkV2Client extends ClientBase<BulkV2Connection> {
public class BulkV2Client extends RESTClient<BulkV2Connection> {
private static Logger LOG = LogManager.getLogger(BulkV2Client.class);
private BulkV2Connection client;
private ConnectorConfig connectorConfig = null;

public BulkV2Client(Controller controller) {
super(controller, LOG);
}

public BulkV2Connection getConnection() {
return client;
}

@Override
protected boolean connectPostLogin(ConnectorConfig cc) {
try {
// Set up a connection object with the given config
this.client = new BulkV2Connection(cc, controller);

setConnection(new BulkV2Connection(cc, controller));
} catch (AsyncApiException e) {
logger.error(Messages.getMessage(getClass(), "loginError", cc.getAuthEndpoint(), e.getExceptionMessage()),
e);
Expand All @@ -62,17 +55,12 @@ protected boolean connectPostLogin(ConnectorConfig cc) {
return true;
}

public synchronized ConnectorConfig getConnectorConfig() {
this.connectorConfig = super.getConnectorConfig();
// override the restEndpoint value set in the superclass
String server = getSession().getServer();
if (server != null) {
this.connectorConfig.setRestEndpoint(server + getServicePath());
}
return this.connectorConfig;
}

protected static String getServicePath() {
public static String getServicePath() {
return "/services/data/v" + getAPIVersionForTheSession() + "/jobs/";
}

@Override
public String getServiceURLPath() {
return getServicePath();
}
}
11 changes: 9 additions & 2 deletions src/main/java/com/salesforce/dataloader/client/ClientBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,20 @@ public abstract class ClientBase<ConnectionType> {
protected final Logger logger;
protected final Controller controller;
protected final Config config;
private ConnectionType connectionType;

private SessionInfo session = new SessionInfo();

protected abstract boolean connectPostLogin(ConnectorConfig connectorConfig);

public abstract ConnectionType getConnection();

public ConnectionType getConnection() {
return this.connectionType;
}

protected void setConnection(ConnectionType connType) {
this.connectionType = connType;
}

protected ClientBase(Controller controller, Logger logger) {
this.controller = controller;
this.config = controller.getConfig();
Expand Down
Loading

0 comments on commit 7ec745a

Please sign in to comment.