diff --git a/pom.xml b/pom.xml
index 30c93637d..af3aa4e09 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.force
dataloader
- 65.0.0
+ 66.0.0
jar
Salesforce Data Loader
https://github.com/forcedotcom/dataloader
@@ -51,7 +51,7 @@
com.force.api
force-partner-api
- 64.0.3
+ 66.0.0
commons-logging
diff --git a/src/main/java/com/salesforce/dataloader/client/ClientBase.java b/src/main/java/com/salesforce/dataloader/client/ClientBase.java
index acb15d62a..887a787a2 100644
--- a/src/main/java/com/salesforce/dataloader/client/ClientBase.java
+++ b/src/main/java/com/salesforce/dataloader/client/ClientBase.java
@@ -136,17 +136,27 @@ public final boolean connect(SessionInfo sess) {
public static final String SFORCE_CALL_OPTIONS_HEADER = "Sforce-Call-Options";
public static String getClientName(AppConfig cfg) {
+ return getClientName(cfg.isBulkAPIEnabled(), cfg.isBulkV2APIEnabled(),
+ cfg.isBatchMode(), cfg.isExternalClientAppConfigured(), Controller.APP_VERSION);
+ }
+
+ static String getClientName(boolean bulkAPI, boolean bulkV2API,
+ boolean batchMode, boolean externalClientApp, String appVersion) {
String apiType = PARTNER_API_CLIENT_TYPE;
- final String interfaceType = cfg.isBatchMode() ? BATCH_CLIENT_STRING : UI_CLIENT_STRING;
- if (cfg.isBulkAPIEnabled()) {
+ if (bulkAPI) {
apiType = BULK_API_CLIENT_TYPE;
- }else if (cfg.isBulkV2APIEnabled()) {
+ } else if (bulkV2API) {
apiType = BULK_V2_API_CLIENT_TYPE;
}
+
+ String interfaceType = externalClientApp
+ ? ""
+ : (batchMode ? BATCH_CLIENT_STRING : UI_CLIENT_STRING);
+
return new StringBuilder(32).append(BASE_CLIENT_NAME).append(apiType).append(interfaceType)
.append("/")
- .append(Controller.APP_VERSION)
- .toString(); //$NON-NLS-1$
+ .append(appVersion)
+ .toString();
}
public static synchronized String getAPIVersionForTheSession() {
diff --git a/src/main/java/com/salesforce/dataloader/client/LoginClient.java b/src/main/java/com/salesforce/dataloader/client/LoginClient.java
index 44a2b1cb3..f85a5c6c0 100644
--- a/src/main/java/com/salesforce/dataloader/client/LoginClient.java
+++ b/src/main/java/com/salesforce/dataloader/client/LoginClient.java
@@ -245,7 +245,7 @@ private void setConfiguredSessionId(PartnerConnection conn, String sessionId, Ge
private void loginInternal(final PartnerConnection conn) throws ConnectionException, PasswordExpiredException {
final ConnectorConfig cc = conn.getConfig();
- cc.setRequestHeader(AppConfig.CLIENT_ID_HEADER_NAME, appConfig.getClientIDForCurrentEnv());
+ cc.setRequestHeader(AppConfig.CLIENT_ID_HEADER_NAME, appConfig.getEffectiveClientIdForCurrentEnv());
try {
logger.info(Messages.getMessage(getClass(), "sforceLoginDetail", cc.getAuthEndpoint(), cc.getUsername()));
LoginResult loginResult = runLoginOperation(conn);
diff --git a/src/main/java/com/salesforce/dataloader/client/transport/HttpTransportImpl.java b/src/main/java/com/salesforce/dataloader/client/transport/HttpTransportImpl.java
index f5afc3f51..c15542b44 100644
--- a/src/main/java/com/salesforce/dataloader/client/transport/HttpTransportImpl.java
+++ b/src/main/java/com/salesforce/dataloader/client/transport/HttpTransportImpl.java
@@ -444,7 +444,7 @@ private void setAuthAndClientHeadersForHttpMethod() {
Header clientIdHeaderVal = this.httpMethod.getFirstHeader(AppConfig.CLIENT_ID_HEADER_NAME);
if (clientIdHeaderVal == null) {
AppConfig appConfig = AppConfig.getCurrentConfig();
- this.httpMethod.addHeader(AppConfig.CLIENT_ID_HEADER_NAME, appConfig.getClientIDForCurrentEnv());
+ this.httpMethod.addHeader(AppConfig.CLIENT_ID_HEADER_NAME, appConfig.getEffectiveClientIdForCurrentEnv());
}
Header clientNameHeaderVal = this.httpMethod.getFirstHeader(ClientBase.SFORCE_CALL_OPTIONS_HEADER);
if (clientNameHeaderVal == null) {
diff --git a/src/main/java/com/salesforce/dataloader/oauth/OAuthFlowHandler.java b/src/main/java/com/salesforce/dataloader/oauth/OAuthFlowHandler.java
index d27b92a5e..a3933c167 100644
--- a/src/main/java/com/salesforce/dataloader/oauth/OAuthFlowHandler.java
+++ b/src/main/java/com/salesforce/dataloader/oauth/OAuthFlowHandler.java
@@ -29,6 +29,7 @@
import com.salesforce.dataloader.config.AppConfig;
import com.salesforce.dataloader.controller.Controller;
import com.salesforce.dataloader.ui.Labels;
+import com.salesforce.dataloader.util.AppUtil;
import com.salesforce.dataloader.util.DLLogManager;
import com.salesforce.dataloader.util.OAuthServerFlow;
import org.apache.logging.log4j.Logger;
@@ -53,6 +54,10 @@ public OAuthFlowHandler(AppConfig appConfig, Consumer statusConsumer, Co
this.loginButtonEnabler = loginButtonEnabler;
}
+ private boolean shouldRunLoginButtonEnabler() {
+ return loginButtonEnabler != null && !AppUtil.isRunningInBatchMode();
+ }
+
/**
* Handles Web Server OAuth flow leveraging Proof Key for Code Exchange(PKCE).
*
@@ -77,8 +82,10 @@ public boolean handleOAuthLogin() {
try {
if (controller.login()) {
controller.saveConfig();
- Display.getDefault().asyncExec(() -> controller.updateLoaderWindowTitleAndCacheUserInfoForTheSession());
- if (loginButtonEnabler != null) {
+ if (!AppUtil.isRunningInBatchMode()) {
+ Display.getDefault().asyncExec(() -> controller.updateLoaderWindowTitleAndCacheUserInfoForTheSession());
+ }
+ if (shouldRunLoginButtonEnabler()) {
Display.getDefault().asyncExec(loginButtonEnabler);
}
return true;
@@ -88,13 +95,13 @@ public boolean handleOAuthLogin() {
if (statusConsumer != null) {
statusConsumer.accept(Labels.getString("OAuthLoginControl.statusControllerUpdateError"));
}
- if (loginButtonEnabler != null) {
+ if (shouldRunLoginButtonEnabler()) {
Display.getDefault().asyncExec(loginButtonEnabler);
}
return false;
}
}
- if (loginButtonEnabler != null) {
+ if (shouldRunLoginButtonEnabler()) {
Display.getDefault().asyncExec(loginButtonEnabler);
}
return true;
@@ -111,7 +118,7 @@ public boolean handleOAuthLogin() {
if (statusConsumer != null) {
statusConsumer.accept(Labels.getString("OAuthLoginControl.statusPKCEFailedFallbackBrowser"));
}
- if (loginButtonEnabler != null) {
+ if (shouldRunLoginButtonEnabler()) {
Display.getDefault().asyncExec(loginButtonEnabler);
}
return false;
diff --git a/src/main/java/com/salesforce/dataloader/util/AppUtil.java b/src/main/java/com/salesforce/dataloader/util/AppUtil.java
index 2ea5799d8..5c21acb45 100644
--- a/src/main/java/com/salesforce/dataloader/util/AppUtil.java
+++ b/src/main/java/com/salesforce/dataloader/util/AppUtil.java
@@ -238,6 +238,10 @@ public static void extractDirFromJar(String extractionPrefix, String destDirName
public static APP_RUN_MODE getAppRunMode() {
return appRunMode;
}
+
+ public static boolean isRunningInBatchMode() {
+ return appRunMode == APP_RUN_MODE.BATCH;
+ }
public static boolean isContentSObject(String sObjectName) {
return CONTENT_SOBJECT_LIST.contains(sObjectName.toLowerCase());
diff --git a/src/test/java/com/salesforce/dataloader/client/ClientBaseGetClientNameTest.java b/src/test/java/com/salesforce/dataloader/client/ClientBaseGetClientNameTest.java
new file mode 100644
index 000000000..5128dc265
--- /dev/null
+++ b/src/test/java/com/salesforce/dataloader/client/ClientBaseGetClientNameTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2015, salesforce.com, inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
+ * promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.salesforce.dataloader.client;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * Unit tests for {@link ClientBase#getClientName}.
+ * Verifies that External Client App configurations produce neutral client names
+ * (without UI/Batch suffix) while legacy configurations retain the original format.
+ */
+public class ClientBaseGetClientNameTest {
+
+ private static final String VERSION = "65.0.0";
+
+ // --- Legacy (no ECA) ---
+
+ @Test
+ public void legacy_partnerUI() {
+ assertEquals("DataLoaderPartnerUI/65.0.0",
+ ClientBase.getClientName(false, false, false, false, VERSION));
+ }
+
+ @Test
+ public void legacy_partnerBatch() {
+ assertEquals("DataLoaderPartnerBatch/65.0.0",
+ ClientBase.getClientName(false, false, true, false, VERSION));
+ }
+
+ @Test
+ public void legacy_bulkUI() {
+ assertEquals("DataLoaderBulkUI/65.0.0",
+ ClientBase.getClientName(true, false, false, false, VERSION));
+ }
+
+ @Test
+ public void legacy_bulkv2Batch() {
+ assertEquals("DataLoaderBulkv2Batch/65.0.0",
+ ClientBase.getClientName(false, true, true, false, VERSION));
+ }
+
+ // --- ECA configured ---
+
+ @Test
+ public void eca_partner() {
+ String name = ClientBase.getClientName(false, false, false, true, VERSION);
+ assertEquals("DataLoaderPartner/65.0.0", name);
+ assertFalse(name.contains("UI"));
+ assertFalse(name.contains("Batch"));
+ }
+
+ @Test
+ public void eca_bulk() {
+ String name = ClientBase.getClientName(true, false, false, true, VERSION);
+ assertEquals("DataLoaderBulk/65.0.0", name);
+ assertFalse(name.contains("UI"));
+ assertFalse(name.contains("Batch"));
+ }
+
+ @Test
+ public void eca_bulkv2() {
+ String name = ClientBase.getClientName(false, true, false, true, VERSION);
+ assertEquals("DataLoaderBulkv2/65.0.0", name);
+ assertFalse(name.contains("UI"));
+ assertFalse(name.contains("Batch"));
+ }
+
+ @Test
+ public void eca_ignoresBatchMode() {
+ String name = ClientBase.getClientName(false, false, true, true, VERSION);
+ assertEquals("DataLoaderPartner/65.0.0", name);
+ assertFalse("ECA path should not include Batch suffix", name.contains("Batch"));
+ }
+}
diff --git a/src/test/java/com/salesforce/dataloader/oauth/OAuthFlowHandlerTest.java b/src/test/java/com/salesforce/dataloader/oauth/OAuthFlowHandlerTest.java
new file mode 100644
index 000000000..bef9db79c
--- /dev/null
+++ b/src/test/java/com/salesforce/dataloader/oauth/OAuthFlowHandlerTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2015, salesforce.com, inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
+ * promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.salesforce.dataloader.oauth;
+
+import com.salesforce.dataloader.config.AppConfig;
+import com.salesforce.dataloader.controller.Controller;
+import com.salesforce.dataloader.util.AppUtil;
+import com.salesforce.dataloader.util.OAuthServerFlow;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockedConstruction;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.*;
+
+/**
+ * Verifies that OAuthFlowHandler works correctly in batch mode
+ * by skipping Display.getDefault() calls that would crash without SWT.
+ */
+public class OAuthFlowHandlerTest {
+
+ private AppUtil.APP_RUN_MODE originalRunMode;
+
+ @Before
+ public void setUp() {
+ originalRunMode = AppUtil.getAppRunMode();
+ }
+
+ @After
+ public void tearDown() {
+ AppUtil.setAppRunMode(originalRunMode.name().toLowerCase());
+ }
+
+ @Test
+ public void testHandleOAuthLoginSucceedsInBatchMode() throws Exception {
+ AppUtil.setAppRunMode("batch");
+
+ Controller mockController = mock(Controller.class);
+ when(mockController.login()).thenReturn(true);
+ AppConfig mockAppConfig = mock(AppConfig.class);
+
+ try (MockedConstruction ignored = mockConstruction(
+ OAuthServerFlow.class,
+ (mock, context) -> when(mock.performOAuthFlow()).thenReturn(true))) {
+
+ assertTrue("Run mode should be BATCH", AppUtil.isRunningInBatchMode());
+
+ OAuthFlowHandler handler = new OAuthFlowHandler(
+ mockAppConfig, status -> {}, mockController, () -> {});
+
+ boolean result = handler.handleOAuthLogin();
+ assertTrue("OAuth login should succeed in batch mode with fix applied", result);
+ }
+ }
+}