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")); + } }