diff --git a/pom.xml b/pom.xml
index af3aa4e0..b58bd907 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.force
dataloader
- 66.0.0
+ 67.0.0
jar
Salesforce Data Loader
https://github.com/forcedotcom/dataloader
@@ -51,7 +51,7 @@
com.force.api
force-partner-api
- 66.0.0
+ 67.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 887a787a..f1d03f5c 100644
--- a/src/main/java/com/salesforce/dataloader/client/ClientBase.java
+++ b/src/main/java/com/salesforce/dataloader/client/ClientBase.java
@@ -34,6 +34,7 @@
import com.salesforce.dataloader.config.AppConfig;
import com.salesforce.dataloader.config.Messages;
import com.salesforce.dataloader.controller.Controller;
+import com.salesforce.dataloader.dao.DataAccessObjectFactory;
import com.salesforce.dataloader.exception.ParameterLoadException;
import com.salesforce.dataloader.util.AppUtil;
import com.sforce.soap.partner.Connector;
@@ -137,11 +138,20 @@ public final boolean connect(SessionInfo sess) {
public static String getClientName(AppConfig cfg) {
return getClientName(cfg.isBulkAPIEnabled(), cfg.isBulkV2APIEnabled(),
- cfg.isBatchMode(), cfg.isExternalClientAppConfigured(), Controller.APP_VERSION);
+ cfg.isBatchMode(), cfg.isExternalClientAppConfigured(), Controller.APP_VERSION,
+ cfg.getString(AppConfig.PROP_DAO_TYPE));
}
+ // Kept for source-compatibility with existing tests; delegates to the 6-arg overload below.
static String getClientName(boolean bulkAPI, boolean bulkV2API,
boolean batchMode, boolean externalClientApp, String appVersion) {
+ return getClientName(bulkAPI, bulkV2API, batchMode, externalClientApp, appVersion, null);
+ }
+
+ // W-22717496: append DAO-type suffix so apusg can measure direct-DB-access usage
+ static String getClientName(boolean bulkAPI, boolean bulkV2API,
+ boolean batchMode, boolean externalClientApp,
+ String appVersion, String daoType) {
String apiType = PARTNER_API_CLIENT_TYPE;
if (bulkAPI) {
apiType = BULK_API_CLIENT_TYPE;
@@ -153,10 +163,18 @@ static String getClientName(boolean bulkAPI, boolean bulkV2API,
? ""
: (batchMode ? BATCH_CLIENT_STRING : UI_CLIENT_STRING);
- return new StringBuilder(32).append(BASE_CLIENT_NAME).append(apiType).append(interfaceType)
+ StringBuilder sb = new StringBuilder(32).append(BASE_CLIENT_NAME).append(apiType).append(interfaceType)
.append("/")
- .append(appVersion)
- .toString();
+ .append(appVersion);
+
+ if (batchMode && daoType != null) {
+ if (DataAccessObjectFactory.DATABASE_READ_TYPE.equalsIgnoreCase(daoType)) {
+ sb.append("/DBR");
+ } else if (DataAccessObjectFactory.DATABASE_WRITE_TYPE.equalsIgnoreCase(daoType)) {
+ sb.append("/DBW");
+ }
+ }
+ return sb.toString();
}
public static synchronized String getAPIVersionForTheSession() {
@@ -172,7 +190,7 @@ public ConnectorConfig getConnectorConfig() {
cc.setTransportFactory(new TransportFactoryImpl());
cc.setSessionId(getSessionId());
cc.setRequestHeader(SFORCE_CALL_OPTIONS_HEADER,
- "client=" + ClientBase.getClientName(this.appConfig));
+ "client=" + ClientBase.getClientName(this.appConfig));
// set authentication credentials
// blank username is not acceptible
String username = appConfig.getString(AppConfig.PROP_USERNAME);
diff --git a/src/test/java/com/salesforce/dataloader/client/ClientBaseGetClientNameTest.java b/src/test/java/com/salesforce/dataloader/client/ClientBaseGetClientNameTest.java
index 5128dc26..e42ace25 100644
--- a/src/test/java/com/salesforce/dataloader/client/ClientBaseGetClientNameTest.java
+++ b/src/test/java/com/salesforce/dataloader/client/ClientBaseGetClientNameTest.java
@@ -25,10 +25,15 @@
*/
package com.salesforce.dataloader.client;
+import com.salesforce.dataloader.config.AppConfig;
+import com.salesforce.dataloader.controller.Controller;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
/**
* Unit tests for {@link ClientBase#getClientName}.
@@ -97,4 +102,131 @@ public void eca_ignoresBatchMode() {
assertEquals("DataLoaderPartner/65.0.0", name);
assertFalse("ECA path should not include Batch suffix", name.contains("Batch"));
}
+
+ // --- DAO type suffix (W-22717496) ---
+
+ @Test
+ public void batch_partner_databaseRead() {
+ assertEquals("DataLoaderPartnerBatch/65.0.0/DBR",
+ ClientBase.getClientName(false, false, true, false, VERSION, "databaseRead"));
+ }
+
+ @Test
+ public void batch_partner_databaseWrite() {
+ assertEquals("DataLoaderPartnerBatch/65.0.0/DBW",
+ ClientBase.getClientName(false, false, true, false, VERSION, "databaseWrite"));
+ }
+
+ @Test
+ public void batch_bulk_databaseRead() {
+ assertEquals("DataLoaderBulkBatch/65.0.0/DBR",
+ ClientBase.getClientName(true, false, true, false, VERSION, "databaseRead"));
+ }
+
+ @Test
+ public void batch_bulkv2_databaseWrite() {
+ assertEquals("DataLoaderBulkv2Batch/65.0.0/DBW",
+ ClientBase.getClientName(false, true, true, false, VERSION, "databaseWrite"));
+ }
+
+ @Test
+ public void batch_csvRead_unchanged() {
+ assertEquals("DataLoaderPartnerBatch/65.0.0",
+ ClientBase.getClientName(false, false, true, false, VERSION, "csvRead"));
+ }
+
+ @Test
+ public void batch_csvWrite_unchanged() {
+ assertEquals("DataLoaderPartnerBatch/65.0.0",
+ ClientBase.getClientName(false, false, true, false, VERSION, "csvWrite"));
+ }
+
+ @Test
+ public void batch_databaseRead_caseInsensitive() {
+ assertEquals("DataLoaderPartnerBatch/65.0.0/DBR",
+ ClientBase.getClientName(false, false, true, false, VERSION, "DATABASEREAD"));
+ }
+
+ @Test
+ public void ui_databaseRead_noSuffix() {
+ assertEquals("DataLoaderPartnerUI/65.0.0",
+ ClientBase.getClientName(false, false, false, false, VERSION, "databaseRead"));
+ }
+
+ @Test
+ public void eca_batch_databaseWrite() {
+ assertEquals("DataLoaderBulk/65.0.0/DBW",
+ ClientBase.getClientName(true, false, true, true, VERSION, "databaseWrite"));
+ }
+
+ // W-19625612 regression guard: ECA + UI + database* must NOT acquire a suffix.
+ // PR 16 requires ECA paths to emit a neutral name that doesn't match any
+ // blocked legacy Connected App; appending /DBR or /DBW to a UI-mode header
+ // would break that contract if the suffix logic ever drifted away from
+ // batchMode-gating.
+ @Test
+ public void eca_ui_databaseRead_noSuffix() {
+ assertEquals("DataLoaderBulk/65.0.0",
+ ClientBase.getClientName(true, false, false, true, VERSION, "databaseRead"));
+ }
+
+ @Test
+ public void eca_ui_databaseWrite_noSuffix() {
+ assertEquals("DataLoaderPartner/65.0.0",
+ ClientBase.getClientName(false, false, false, true, VERSION, "databaseWrite"));
+ }
+
+ @Test
+ public void dao_type_null_unchanged() {
+ assertEquals("DataLoaderPartnerBatch/65.0.0",
+ ClientBase.getClientName(false, false, true, false, VERSION, (String) null));
+ }
+
+ // --- AppConfig wiring (W-22717496): the single-arg overload must read PROP_DAO_TYPE ---
+
+ private AppConfig mockCfg(boolean bulk, boolean bulkV2, boolean batch, boolean eca, String daoType) {
+ AppConfig cfg = mock(AppConfig.class);
+ when(cfg.isBulkAPIEnabled()).thenReturn(bulk);
+ when(cfg.isBulkV2APIEnabled()).thenReturn(bulkV2);
+ when(cfg.isBatchMode()).thenReturn(batch);
+ when(cfg.isExternalClientAppConfigured()).thenReturn(eca);
+ when(cfg.getString(AppConfig.PROP_DAO_TYPE)).thenReturn(daoType);
+ return cfg;
+ }
+
+ @Test
+ public void appConfig_batch_databaseRead_emitsDBR() {
+ String name = ClientBase.getClientName(mockCfg(true, false, true, false, "databaseRead"));
+ assertTrue("expected /DBR suffix, got: " + name, name.endsWith("/DBR"));
+ assertTrue(name.startsWith("DataLoaderBulkBatch/"));
+ }
+
+ @Test
+ public void appConfig_batch_databaseWrite_emitsDBW() {
+ String name = ClientBase.getClientName(mockCfg(false, true, true, false, "databaseWrite"));
+ assertTrue("expected /DBW suffix, got: " + name, name.endsWith("/DBW"));
+ assertTrue(name.startsWith("DataLoaderBulkv2Batch/"));
+ }
+
+ @Test
+ public void appConfig_batch_csvRead_noSuffix() {
+ String name = ClientBase.getClientName(mockCfg(false, false, true, false, "csvRead"));
+ assertFalse("CSV must not get DAO suffix, got: " + name, name.endsWith("/DBR"));
+ assertFalse(name.endsWith("/DBW"));
+ assertEquals("DataLoaderPartnerBatch/" + Controller.APP_VERSION, name);
+ }
+
+ @Test
+ public void appConfig_ui_databaseRead_noSuffix() {
+ String name = ClientBase.getClientName(mockCfg(false, false, false, false, "databaseRead"));
+ assertFalse("UI mode must not get DAO suffix, got: " + name, name.endsWith("/DBR"));
+ assertEquals("DataLoaderPartnerUI/" + Controller.APP_VERSION, name);
+ }
+
+ @Test
+ public void appConfig_batch_nullDaoType_noSuffix() {
+ String name = ClientBase.getClientName(mockCfg(false, false, true, false, null));
+ assertFalse(name.endsWith("/DBR"));
+ assertFalse(name.endsWith("/DBW"));
+ }
}