diff --git a/docs/SuccessFactors-batchsource.md b/docs/SuccessFactors-batchsource.md index 05df6e6..5f4d395 100644 --- a/docs/SuccessFactors-batchsource.md +++ b/docs/SuccessFactors-batchsource.md @@ -23,6 +23,11 @@ You also can use the macro function ${conn(connection-name)}. **SAP SuccessFactors Logon Password (M)**: SAP SuccessFactors Logon password for user authentication. **SAP SuccessFactors Base URL (M)**: SAP SuccessFactors Base URL. +## Proxy Configuration +**Proxy URL:** Proxy URL. Must contain a protocol, address and port. +**Username:** Proxy username. +**Password:** Proxy password. + ## Advance Option: diff --git a/docs/SuccessFactors-connector.md b/docs/SuccessFactors-connector.md index 3430674..9fff9ed 100644 --- a/docs/SuccessFactors-connector.md +++ b/docs/SuccessFactors-connector.md @@ -16,6 +16,12 @@ Properties **SAP SuccessFactors Base URL (M)**: SAP SuccessFactors Base URL. +**Proxy URL:** Proxy URL. Must contain a protocol, address and port. + +**Username:** Proxy username. + +**Password:** Proxy password. + Path of the connection ---------------------- diff --git a/src/main/java/io/cdap/plugin/successfactors/common/util/SuccessFactorsUtil.java b/src/main/java/io/cdap/plugin/successfactors/common/util/SuccessFactorsUtil.java index e646586..da2298f 100644 --- a/src/main/java/io/cdap/plugin/successfactors/common/util/SuccessFactorsUtil.java +++ b/src/main/java/io/cdap/plugin/successfactors/common/util/SuccessFactorsUtil.java @@ -97,8 +97,7 @@ public static String trim(String rawString) { * @return SuccessFactorsService instance */ public static SuccessFactorsService getSuccessFactorsService(SuccessFactorsPluginConfig pluginConfig) { - SuccessFactorsTransporter transporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), - pluginConfig.getConnection().getPassword()); + SuccessFactorsTransporter transporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); SuccessFactorsService successFactorsService = new SuccessFactorsService(pluginConfig, transporter); return successFactorsService; } diff --git a/src/main/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnector.java b/src/main/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnector.java index 6f4e723..da10995 100644 --- a/src/main/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnector.java +++ b/src/main/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnector.java @@ -149,8 +149,7 @@ public ConnectorSpec generateSpec(ConnectorContext connectorContext, ConnectorSp List listEntities() throws TransportException, IOException { URL dataURL = HttpUrl.parse(config.getBaseURL()).newBuilder().build().url(); - SuccessFactorsTransporter successFactorsHttpClient = new SuccessFactorsTransporter(config.getUsername(), - config.getPassword()); + SuccessFactorsTransporter successFactorsHttpClient = new SuccessFactorsTransporter(config); SuccessFactorsResponseContainer responseContainer = successFactorsHttpClient.callSuccessFactorsEntity (dataURL, MediaType.APPLICATION_JSON, METADATA); try (InputStream inputStream = responseContainer.getResponseStream()) { @@ -225,8 +224,7 @@ private InputStream callEntityData(long top, String entityName) URL dataURL = HttpUrl.parse(config.getBaseURL()).newBuilder().addPathSegment(entityName). addQueryParameter(TOP_OPTION, String.valueOf(top)).addQueryParameter(SELECT_OPTION, selectFields.toString()) .build().url(); - SuccessFactorsTransporter successFactorsHttpClient = new SuccessFactorsTransporter(config.getUsername(), - config.getPassword()); + SuccessFactorsTransporter successFactorsHttpClient = new SuccessFactorsTransporter(config); SuccessFactorsResponseContainer responseContainer = successFactorsHttpClient.callSuccessFactorsWithRetry(dataURL); ExceptionParser.checkAndThrowException("", responseContainer); @@ -255,8 +253,7 @@ SuccessFactorsEntityProvider fetchServiceMetadata(String entity) throws Transpor private InputStream getMetaDataStream(String entity) throws TransportException, IOException { URL metadataURL = HttpUrl.parse(config.getBaseURL()).newBuilder().addPathSegments(entity) .addPathSegment(METADATACALL).build().url(); - SuccessFactorsTransporter successFactorsHttpClient = new SuccessFactorsTransporter(config.getUsername(), - config.getPassword()); + SuccessFactorsTransporter successFactorsHttpClient = new SuccessFactorsTransporter(config); SuccessFactorsResponseContainer responseContainer = successFactorsHttpClient .callSuccessFactorsEntity(metadataURL, MediaType.APPLICATION_XML, METADATA); return responseContainer.getResponseStream(); diff --git a/src/main/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnectorConfig.java b/src/main/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnectorConfig.java index 50f96c5..fe41190 100644 --- a/src/main/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnectorConfig.java +++ b/src/main/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnectorConfig.java @@ -33,6 +33,8 @@ import java.net.HttpURLConnection; import java.net.URL; + +import javax.annotation.Nullable; import javax.ws.rs.core.MediaType; /** @@ -43,6 +45,9 @@ public class SuccessFactorsConnectorConfig extends PluginConfig { public static final String BASE_URL = "baseURL"; public static final String UNAME = "username"; public static final String PASSWORD = "password"; + public static final String PROPERTY_PROXY_URL = "proxyUrl"; + public static final String PROPERTY_PROXY_USERNAME = "proxyUsername"; + public static final String PROPERTY_PROXY_PASSWORD = "proxyPassword"; public static final String TEST = "TEST"; private static final String COMMON_ACTION = ResourceConstants.ERR_MISSING_PARAM_OR_MACRO_ACTION.getMsgForKey(); private static final String SAP_SUCCESSFACTORS_USERNAME = "SAP SuccessFactors Username"; @@ -65,10 +70,44 @@ public class SuccessFactorsConnectorConfig extends PluginConfig { @Description("SuccessFactors Base URL.") private final String baseURL; - public SuccessFactorsConnectorConfig(String username, String password, String baseURL) { + @Nullable + @Name(PROPERTY_PROXY_URL) + @Description("Proxy URL. Must contain a protocol, address and port.") + @Macro + protected String proxyUrl; + + @Nullable + @Name(PROPERTY_PROXY_USERNAME) + @Description("Proxy username.") + @Macro + protected String proxyUsername; + + @Nullable + @Name(PROPERTY_PROXY_PASSWORD) + @Description("Proxy password.") + @Macro + protected String proxyPassword; + + public SuccessFactorsConnectorConfig(String username, String password, String baseURL, String proxyUrl, + String proxyUsername, String proxyPassword) { this.username = username; this.password = password; this.baseURL = baseURL; + this.proxyUrl = proxyUrl; + this.proxyUsername = proxyUsername; + this.proxyPassword = proxyPassword; + } + + public String getProxyUrl() { + return proxyUrl; + } + + public String getProxyUsername() { + return proxyUsername; + } + + public String getProxyPassword() { + return proxyPassword; } public String getUsername() { @@ -109,7 +148,7 @@ public void validateBasicCredentials(FailureCollector failureCollector) { * Method to validate the credential fields. */ public void validateConnection(FailureCollector collector) { - SuccessFactorsTransporter successFactorsHttpClient = new SuccessFactorsTransporter(getUsername(), getPassword()); + SuccessFactorsTransporter successFactorsHttpClient = new SuccessFactorsTransporter(this); URL testerURL = HttpUrl.parse(getBaseURL()).newBuilder().build().url(); SuccessFactorsResponseContainer responseContainer = null; try { diff --git a/src/main/java/io/cdap/plugin/successfactors/source/SuccessFactorsSource.java b/src/main/java/io/cdap/plugin/successfactors/source/SuccessFactorsSource.java index ec8087e..48ab3cc 100644 --- a/src/main/java/io/cdap/plugin/successfactors/source/SuccessFactorsSource.java +++ b/src/main/java/io/cdap/plugin/successfactors/source/SuccessFactorsSource.java @@ -126,8 +126,7 @@ public void prepareRun(BatchSourceContext context) throws Exception { @Nullable private Schema getOutputSchema(FailureCollector failureCollector) { if (config.getConnection() != null) { - SuccessFactorsTransporter transporter = new SuccessFactorsTransporter(config.getConnection().getUsername(), - config.getConnection().getPassword()); + SuccessFactorsTransporter transporter = new SuccessFactorsTransporter(config.getConnection()); SuccessFactorsService successFactorsServices = new SuccessFactorsService(config, transporter); try { //validate if the given parameters form a valid SuccessFactors URL. diff --git a/src/main/java/io/cdap/plugin/successfactors/source/config/SuccessFactorsPluginConfig.java b/src/main/java/io/cdap/plugin/successfactors/source/config/SuccessFactorsPluginConfig.java index c03bcf9..a670d54 100644 --- a/src/main/java/io/cdap/plugin/successfactors/source/config/SuccessFactorsPluginConfig.java +++ b/src/main/java/io/cdap/plugin/successfactors/source/config/SuccessFactorsPluginConfig.java @@ -43,6 +43,9 @@ public class SuccessFactorsPluginConfig extends PluginConfig { public static final String ENTITY_NAME = "entityName"; public static final String UNAME = "username"; public static final String PASSWORD = "password"; + public static final String PROPERTY_PROXY_URL = "proxyUrl"; + public static final String PROPERTY_PROXY_USERNAME = "proxyUsername"; + public static final String PROPERTY_PROXY_PASSWORD = "proxyPassword"; private static final String REFERENCE_NAME = "referenceName"; private static final String REFERENCE_NAME_DESCRIPTION = "This will be used to uniquely identify this source/sink " + "for lineage, annotating metadata, etc."; @@ -135,12 +138,16 @@ public SuccessFactorsPluginConfig(String referenceName, String associateEntityName, @Nullable String username, @Nullable String password, + @Nullable String proxyUrl, + @Nullable String proxyPassword, + @Nullable String proxyUsername, @Nullable String filterOption, @Nullable String selectOption, @Nullable String expandOption, @Nullable String additionalQueryParameters, String paginationType) { - this.connection = new SuccessFactorsConnectorConfig(username, password, baseURL); + this.connection = new SuccessFactorsConnectorConfig(username, password, baseURL, proxyUrl, proxyPassword, + proxyUsername); this.referenceName = referenceName; this.entityName = entityName; this.associateEntityName = associateEntityName; @@ -214,7 +221,9 @@ public String getAdditionalQueryParameters() { * @return boolean flag as per the check */ public boolean isSchemaBuildRequired() { - return !(containsMacro(UNAME) || containsMacro(PASSWORD) || containsMacro(BASE_URL) || containsMacro(ENTITY_NAME)); + return !(containsMacro(UNAME) || containsMacro(PASSWORD) || containsMacro(BASE_URL) || containsMacro(ENTITY_NAME) + || containsMacro(PROPERTY_PROXY_URL) || containsMacro(PROPERTY_PROXY_USERNAME) + || containsMacro(PROPERTY_PROXY_USERNAME)); } /** @@ -300,6 +309,10 @@ public static class Builder { private String expandOption; private String paginationType; private String additionalQueryParameters; + private String proxyUrl; + private String proxyUsername; + private String proxyPassword; + public Builder referenceName(String referenceName) { this.referenceName = referenceName; @@ -331,6 +344,19 @@ public Builder password(@Nullable String password) { return this; } + public Builder proxyUrl(@Nullable String proxyUrl) { + this.proxyUrl = proxyUrl; + return this; + } + public Builder proxyUsername(@Nullable String proxyUsername) { + this.proxyUsername = proxyUsername; + return this; + } + public Builder proxyPassword(@Nullable String proxyPassword) { + this.proxyPassword = proxyPassword; + return this; + } + public Builder filterOption(@Nullable String filterOption) { this.filterOption = filterOption; return this; @@ -357,7 +383,8 @@ public Builder additionalQueryParameters(@Nullable String additionalQueryParamet } public SuccessFactorsPluginConfig build() { - return new SuccessFactorsPluginConfig(referenceName, baseURL, entityName, associateEntityName, username, password, + return new SuccessFactorsPluginConfig(referenceName, baseURL, entityName, associateEntityName, username, + password, proxyUrl, proxyUsername, proxyPassword, filterOption, selectOption, expandOption, additionalQueryParameters, paginationType); } diff --git a/src/main/java/io/cdap/plugin/successfactors/source/transport/SuccessFactorsTransporter.java b/src/main/java/io/cdap/plugin/successfactors/source/transport/SuccessFactorsTransporter.java index ec30cd4..0064643 100644 --- a/src/main/java/io/cdap/plugin/successfactors/source/transport/SuccessFactorsTransporter.java +++ b/src/main/java/io/cdap/plugin/successfactors/source/transport/SuccessFactorsTransporter.java @@ -24,22 +24,34 @@ import io.cdap.cdap.api.retry.RetryableException; import io.cdap.plugin.successfactors.common.exception.TransportException; import io.cdap.plugin.successfactors.common.util.ResourceConstants; +import io.cdap.plugin.successfactors.connector.SuccessFactorsConnectorConfig; +import okhttp3.Authenticator; +import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; +import okhttp3.Route; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.Proxy; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; + import javax.ws.rs.core.MediaType; + + + /** * This {@code SuccessFactorsTransporter} class is used to * make a rest web service call to the SAP SuccessFactors exposed services. @@ -50,14 +62,11 @@ public class SuccessFactorsTransporter { private static final long CONNECTION_TIMEOUT = 300; private static final long WAIT_TIME = 5; private static final long MAX_NUMBER_OF_RETRY_ATTEMPTS = 5; - - private final String username; - private final String password; + private SuccessFactorsConnectorConfig config; private Response response; - public SuccessFactorsTransporter(String username, String password) { - this.username = username; - this.password = password; + public SuccessFactorsTransporter(SuccessFactorsConnectorConfig pluginConfig) { + this.config = pluginConfig; } @@ -152,7 +161,8 @@ public Response retrySapTransportCall(URL endpoint, String mediaType) throws IOE * @throws TransportException any error while preparing the {@code OkHttpClient} */ private Response transport(URL endpoint, String mediaType) throws IOException, TransportException { - OkHttpClient enhancedOkHttpClient = getConfiguredClient().build(); + OkHttpClient enhancedOkHttpClient = + buildConfiguredClient(config.getProxyUrl(), config.getProxyUsername(), config.getProxyPassword()).build(); Request req = buildRequest(endpoint, mediaType); return enhancedOkHttpClient.newCall(req).execute(); @@ -177,7 +187,7 @@ private SuccessFactorsResponseContainer prepareResponseContainer(Response res) t /** * Prepares request for metadata and data calls. * - * @param mediaType supported types 'application/json' & 'application/xml' + // * @param mediaType supported types 'application/json' & 'application/xml' * @return Request */ private Request buildRequest(URL endpoint, String mediaType) { @@ -189,6 +199,31 @@ private Request buildRequest(URL endpoint, String mediaType) { .build(); } + private OkHttpClient.Builder buildConfiguredClient(String proxyUrl, String proxyUsername, String proxyPassword) + throws MalformedURLException, TransportException { + OkHttpClient.Builder builder = getConfiguredClient(); + + if (proxyUrl != null && !proxyUrl.isEmpty()) { + Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(new URL(proxyUrl).getHost(), + new URL(proxyUrl).getPort())); + builder.proxy(proxy); + + if (proxyUsername != null && !proxyUsername.isEmpty() && proxyPassword != null && !proxyPassword.isEmpty()) { + builder.proxyAuthenticator(new Authenticator() { + @Override + public Request authenticate(Route route, Response response) { + String credential = Credentials.basic(proxyUsername, proxyPassword); + return response.request().newBuilder() + .header("Proxy-Authorization", credential) + .build(); + } + }); + } + } + + return builder; + } + /** * Builds the {@code OkHttpClient.Builder} with following optimized configuration parameters as per the SAP Gateway * recommendations. @@ -218,9 +253,9 @@ private OkHttpClient.Builder getConfiguredClient() throws TransportException { */ private String getAuthenticationKey() { return "Basic " + Base64.getEncoder() - .encodeToString(username + .encodeToString(config.getUsername() .concat(":") - .concat(password) + .concat(config.getPassword()) .getBytes(StandardCharsets.UTF_8) ); } diff --git a/src/test/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnectorTest.java b/src/test/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnectorTest.java index acfbfb8..6f8dc57 100644 --- a/src/test/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnectorTest.java +++ b/src/test/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnectorTest.java @@ -85,8 +85,7 @@ public void testConfiguration() throws TransportException, SuccessFactorsService @Test public void testValidateSuccessfulConnection() throws TransportException, SuccessFactorsServiceException { - successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), - pluginConfig.getConnection().getPassword()); + successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); new Expectations(SuccessFactorsUrlContainer.class, SuccessFactorsTransporter.class, SuccessFactorsSchemaGenerator.class) { { @@ -103,8 +102,7 @@ public void testValidateSuccessfulConnection() throws TransportException, Succes public void testValidateUnauthorisedConnection() throws TransportException, SuccessFactorsServiceException { MockFailureCollector collector = new MockFailureCollector(); ConnectorContext context = new MockConnectorContext(new MockConnectorConfigurer()); - successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), - pluginConfig.getConnection().getPassword()); + successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); new Expectations(SuccessFactorsUrlContainer.class, SuccessFactorsTransporter.class, SuccessFactorsSchemaGenerator.class) { { @@ -120,8 +118,7 @@ public void testValidateUnauthorisedConnection() throws TransportException, Succ @Test public void testValidateNotFoundConnection() throws TransportException, SuccessFactorsServiceException { MockFailureCollector collector = new MockFailureCollector(); - successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), - pluginConfig.getConnection().getPassword()); + successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); new Expectations(SuccessFactorsUrlContainer.class, SuccessFactorsTransporter.class, SuccessFactorsSchemaGenerator.class) { { @@ -154,8 +151,7 @@ private SuccessFactorsResponseContainer getNotFoundResponseContainer() { public void testGenerateSpec() throws TransportException, IOException { ConnectorContext context = new MockConnectorContext(new MockConnectorConfigurer()); MockFailureCollector collector = new MockFailureCollector(); - successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), - pluginConfig.getConnection().getPassword()); + successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); new Expectations(SuccessFactorsTransporter.class) { { successFactorsTransporter.callSuccessFactorsEntity(null, anyString, anyString); @@ -190,8 +186,7 @@ public void testGenerateSpecWithSchema() throws TransportException, IOException, ConnectorContext context = new MockConnectorContext(new MockConnectorConfigurer()); MockFailureCollector collector = new MockFailureCollector(); successFactorsConnector = new SuccessFactorsConnector(pluginConfig.getConnection()); - successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), - pluginConfig.getConnection().getPassword()); + successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); new Expectations(SuccessFactorsConnector.class, SuccessFactorsTransporter.class) { { @@ -232,8 +227,7 @@ public void testBrowse() throws IOException, TransportException { ConnectorContext context = new MockConnectorContext(new MockConnectorConfigurer()); List entities = new ArrayList<>(); entities.add("Achievement"); - successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), - pluginConfig.getConnection().getPassword()); + successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); successFactorsConnector = new SuccessFactorsConnector(pluginConfig.getConnection()); new Expectations(SuccessFactorsTransporter.class, SuccessFactorsTransporter.class, SuccessFactorsConnector.class) { @@ -267,8 +261,7 @@ public void testBrowse() throws IOException, TransportException { @Test(expected = IOException.class) public void testSampleWithoutSampleData() throws IOException, TransportException { ConnectorContext context = new MockConnectorContext(new MockConnectorConfigurer()); - successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), - pluginConfig.getConnection().getPassword()); + successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); new Expectations(SuccessFactorsTransporter.class, SuccessFactorsTransporter.class, SuccessFactorsConnector.class) { { successFactorsTransporter.callSuccessFactorsEntity(null, anyString, anyString); @@ -291,8 +284,7 @@ public void testSampleWithoutSampleData() throws IOException, TransportException @Test public void testSampleWithSampleData() throws IOException, TransportException, EntityProviderException, SuccessFactorsServiceException, EdmException { - successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), - pluginConfig.getConnection().getPassword()); + successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); String entityName = "entity"; List records = new ArrayList<>(); StructuredRecord structuredRecord = Mockito.mock(StructuredRecord.class); diff --git a/src/test/java/io/cdap/plugin/successfactors/source/SuccessFactorsSourceTest.java b/src/test/java/io/cdap/plugin/successfactors/source/SuccessFactorsSourceTest.java index c496581..d19f1ab 100644 --- a/src/test/java/io/cdap/plugin/successfactors/source/SuccessFactorsSourceTest.java +++ b/src/test/java/io/cdap/plugin/successfactors/source/SuccessFactorsSourceTest.java @@ -160,8 +160,7 @@ public void testConfigurePipelineWSchemaNotNull() throws SuccessFactorsServiceEx .password("password"); pluginConfig = pluginConfigBuilder.build(); - successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), - pluginConfig.getConnection().getPassword()); + successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); successFactorsUrlContainer = new SuccessFactorsUrlContainer(pluginConfig); successFactorsSchemaGenerator = new SuccessFactorsSchemaGenerator(new SuccessFactorsEntityProvider(edm)); @@ -267,7 +266,6 @@ private List getSplits() { public void testPrepareRun() throws Exception { successFactorsService = new SuccessFactorsService(pluginConfig, null); successFactorsPartitionBuilder = new SuccessFactorsPartitionBuilder(); - successFactorsTransporter = new SuccessFactorsTransporter("username", "password"); pluginConfigBuilder = SuccessFactorsPluginConfig.builder() .referenceName("unit-test-ref-name") .baseURL("http://localhost") @@ -279,7 +277,7 @@ public void testPrepareRun() throws Exception { .filterOption("$topeq2"); pluginConfig = pluginConfigBuilder.build(); - + successFactorsTransporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); new Expectations(SuccessFactorsService.class, SuccessFactorsTransporter.class) { { context.getOutputSchema(); diff --git a/src/test/java/io/cdap/plugin/successfactors/source/input/SuccessFactorsInputFormatTest.java b/src/test/java/io/cdap/plugin/successfactors/source/input/SuccessFactorsInputFormatTest.java index 5124e28..fd63057 100644 --- a/src/test/java/io/cdap/plugin/successfactors/source/input/SuccessFactorsInputFormatTest.java +++ b/src/test/java/io/cdap/plugin/successfactors/source/input/SuccessFactorsInputFormatTest.java @@ -51,7 +51,8 @@ public void initializeTests() { "entityName", null, "username", - "password", + "password", null, null, + null, "filterOption", "selectOption", "expandOption", diff --git a/src/test/java/io/cdap/plugin/successfactors/source/metadata/SuccessFactorsSchemaGeneratorTest.java b/src/test/java/io/cdap/plugin/successfactors/source/metadata/SuccessFactorsSchemaGeneratorTest.java index b95ee5e..4dff3ee 100644 --- a/src/test/java/io/cdap/plugin/successfactors/source/metadata/SuccessFactorsSchemaGeneratorTest.java +++ b/src/test/java/io/cdap/plugin/successfactors/source/metadata/SuccessFactorsSchemaGeneratorTest.java @@ -80,7 +80,8 @@ public void testSelectWithExpandNames() throws SuccessFactorsServiceException { public void testBuildExpandOutputSchema() throws SuccessFactorsServiceException { SuccessFactorsPluginConfig pluginConfig = new SuccessFactorsPluginConfig("referenceName", "baseUR", "entityName", "associateEntityName", "username", - "password", "filterOption", "selectOption", "expandOption", + "password", null, null, null, "filterOption", + "selectOption", "expandOption", "additionalQueryParameters", "paginationType"); Schema outputSchema = generator.buildExpandOutputSchema("Benefit", "eligibleBenefits", "associatedEntity", pluginConfig); @@ -154,8 +155,9 @@ public void testInvalidEntityName() throws SuccessFactorsServiceException { public void testInvalidExpandName() throws SuccessFactorsServiceException { SuccessFactorsPluginConfig pluginConfig = new SuccessFactorsPluginConfig("referenceName", "baseUR", "entityName", "associateEntityName", "username", - "password", "filterOption", "selectOption", "expandOption", - "additionalQueryParameters", "paginationType"); + "password", null, null, null, "filterOption", + "selectOption", "expandOption", "additionalQueryParameters", + "paginationType"); exception.expectMessage("'assEntity' not found in the 'Benefit' entity."); generator.buildExpandOutputSchema("Benefit", "INVALID-NAVIGATION-NAME", "assEntity", pluginConfig); diff --git a/src/test/java/io/cdap/plugin/successfactors/source/transport/RuntimeFunctionalForAssociatedEntityTest.java b/src/test/java/io/cdap/plugin/successfactors/source/transport/RuntimeFunctionalForAssociatedEntityTest.java index 928c7a1..31cf59c 100644 --- a/src/test/java/io/cdap/plugin/successfactors/source/transport/RuntimeFunctionalForAssociatedEntityTest.java +++ b/src/test/java/io/cdap/plugin/successfactors/source/transport/RuntimeFunctionalForAssociatedEntityTest.java @@ -88,8 +88,7 @@ public void testRecordReaderForAssociatedEntity() throws Exception { long availableRowCount = 1; List partitionList = new SuccessFactorsPartitionBuilder().buildSplits(availableRowCount); - transporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), pluginConfig. - getConnection().getPassword()); + transporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); successFactorsService = new SuccessFactorsService(pluginConfig, transporter); prepareStubForMetadata(); edmData = successFactorsService.getSuccessFactorsServiceEdm(encodedMetadataString); diff --git a/src/test/java/io/cdap/plugin/successfactors/source/transport/RuntimeFunctionalTest.java b/src/test/java/io/cdap/plugin/successfactors/source/transport/RuntimeFunctionalTest.java index 847f94a..1de10ca 100644 --- a/src/test/java/io/cdap/plugin/successfactors/source/transport/RuntimeFunctionalTest.java +++ b/src/test/java/io/cdap/plugin/successfactors/source/transport/RuntimeFunctionalTest.java @@ -104,8 +104,7 @@ public void runPipelineWithDefaultValues() throws Exception { long availableRowCount = 3; List partitionList = new SuccessFactorsPartitionBuilder().buildSplits(availableRowCount); - transporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), pluginConfig. - getConnection().getPassword()); + transporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); successFactorsService = new SuccessFactorsService(pluginConfig, transporter); prepareStubForMetadata(pluginConfig); edmData = successFactorsService.getSuccessFactorsServiceEdm(encodedMetadataString); @@ -141,8 +140,7 @@ public void verifyFailToDecodeMetadataString() throws SuccessFactorsServiceExcep exceptionRule.expect(SuccessFactorsServiceException.class); exceptionRule .expectMessage(ResourceConstants.ERR_METADATA_DECODE.getMsgForKeyWithCode(pluginConfig.getEntityName())); - transporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), pluginConfig. - getConnection().getPassword()); + transporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); successFactorsService = new SuccessFactorsService(pluginConfig, transporter); successFactorsService.getSuccessFactorsServiceEdm("encodedMetadataString"); } @@ -154,8 +152,7 @@ public void verifyDataCorrectness() prepareStubForMetadata(pluginConfig); long availableRowCount = 3; List partitionList = new SuccessFactorsPartitionBuilder().buildSplits(availableRowCount); - transporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), - pluginConfig.getConnection().getPassword()); + transporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); successFactorsService = new SuccessFactorsService(pluginConfig, transporter); edmData = successFactorsService.getSuccessFactorsServiceEdm(encodedMetadataString); for (SuccessFactorsInputSplit inputSplit : partitionList) { diff --git a/src/test/java/io/cdap/plugin/successfactors/source/transport/SuccessFactorsTransporterTest.java b/src/test/java/io/cdap/plugin/successfactors/source/transport/SuccessFactorsTransporterTest.java index 8cf5dcc..7d8fe20 100644 --- a/src/test/java/io/cdap/plugin/successfactors/source/transport/SuccessFactorsTransporterTest.java +++ b/src/test/java/io/cdap/plugin/successfactors/source/transport/SuccessFactorsTransporterTest.java @@ -69,7 +69,7 @@ public class SuccessFactorsTransporterTest { public static void classSetup() { new MockUp() { @Mock - public OkHttpClient.Builder getConfiguredClient() { + public OkHttpClient.Builder buildConfiguredClient(String proxyUrl, String proxyUsername, String proxyPassword) { return allowAllSSL(); } }; @@ -125,8 +125,7 @@ public void setUp() { .expandOption("Products/Supplier"); pluginConfig = pluginConfigBuilder.build(); successFactorsURL = new SuccessFactorsUrlContainer(pluginConfig); - transporter = new SuccessFactorsTransporter(pluginConfig.getConnection().getUsername(), - pluginConfig.getConnection().getPassword()); + transporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); } @Test @@ -153,6 +152,42 @@ public void testCallSuccessFactors() throws TransportException { Assert.assertEquals("HTTP status is same", "OK", response.getHttpStatusMsg()); } + @Test + public void testCallSuccessFactorsWithProxy() throws TransportException { + pluginConfigBuilder = SuccessFactorsPluginConfig.builder() + .baseURL("https://localhost:" + wireMockRule.httpsPort()) + .entityName("Entity") + .username("test") + .password("secret") + .proxyUrl("https://proxy") + .proxyUsername("user") + .proxyPassword("password") + .expandOption("Products/Supplier"); + pluginConfig = pluginConfigBuilder.build(); + successFactorsURL = new SuccessFactorsUrlContainer(pluginConfig); + transporter = new SuccessFactorsTransporter(pluginConfig.getConnection()); + String expectedBody = "{\"d\": [{\"ID\": 0,\"Name\": \"Bread\"}}]}"; + WireMock.stubFor(WireMock.get("/Entity?%24expand=Products%2FSupplier&%24top=1") + .withBasicAuth(pluginConfig.getConnection().getUsername(), + pluginConfig.getConnection().getPassword()) + .willReturn(WireMock.ok() + .withHeader(SuccessFactorsTransporter.SERVICE_VERSION, "2.0") + .withBody(expectedBody))); + SuccessFactorsResponseContainer response = transporter + .callSuccessFactors(successFactorsURL.getTesterURL(), MediaType.APPLICATION_JSON, SuccessFactorsService.TEST); + + Assert.assertEquals("SuccessFactors Service data version is same.", + "2.0", + response.getDataServiceVersion()); + Assert.assertEquals("HTTP status code is same.", + HttpURLConnection.HTTP_OK, + response.getHttpStatusCode()); + Assert.assertEquals("HTTP response body is same.", + expectedBody, + TestSuccessFactorsUtil.convertInputStreamToString(response.getResponseStream())); + Assert.assertEquals("HTTP status is same", "OK", response.getHttpStatusMsg()); + } + @Test public void testUnAuthorized() throws TransportException { WireMock.stubFor(WireMock.get("/Entity/$metadata") diff --git a/src/test/java/io/cdap/plugin/successfactors/source/transport/SuccessFactorsUrlContainerTest.java b/src/test/java/io/cdap/plugin/successfactors/source/transport/SuccessFactorsUrlContainerTest.java index 57f58e9..540c3c9 100644 --- a/src/test/java/io/cdap/plugin/successfactors/source/transport/SuccessFactorsUrlContainerTest.java +++ b/src/test/java/io/cdap/plugin/successfactors/source/transport/SuccessFactorsUrlContainerTest.java @@ -32,7 +32,8 @@ public void initializeTests() { "entityName", "associatedEntity", "username", - "password", + "password", null, null, + null, "filterOption", "selectOption", "expandOption", @@ -71,7 +72,8 @@ public void testGetURLWithAdditionalQueryParameters() { "EmpJob", "associatedEntity", "username", - "password", + "password", null, null, + null, "", "", "", diff --git a/widgets/SuccessFactors-batchsource.json b/widgets/SuccessFactors-batchsource.json index cd4523b..8cb6189 100644 --- a/widgets/SuccessFactors-batchsource.json +++ b/widgets/SuccessFactors-batchsource.json @@ -93,6 +93,26 @@ } ] }, + { + "label": "Proxy Configuration", + "properties": [ + { + "widget-type": "textbox", + "label": "Proxy URL", + "name": "proxyUrl" + }, + { + "widget-type": "textbox", + "label": "Username", + "name": "proxyUsername" + }, + { + "widget-type": "password", + "label": "Password", + "name": "proxyPassword" + } + ] + }, { "label": "Advanced", "properties": [ @@ -176,6 +196,18 @@ { "type": "property", "name": "baseURL" + }, + { + "type": "property", + "name": "proxyUrl" + }, + { + "type": "property", + "name": "proxyUsername" + }, + { + "type": "property", + "name": "proxyPassword" } ] }, @@ -190,6 +222,23 @@ "name": "connection" } ] + }, + { + "name": "Proxy authentication", + "condition": { + "property": "proxyUrl", + "operator": "exists" + }, + "show": [ + { + "name": "proxyUsername", + "type": "property" + }, + { + "name": "proxyPassword", + "type": "property" + } + ] } ], "outputs": [ diff --git a/widgets/SuccessFactors-connector.json b/widgets/SuccessFactors-connector.json index 7ab0324..7ffbc4a 100644 --- a/widgets/SuccessFactors-connector.json +++ b/widgets/SuccessFactors-connector.json @@ -32,6 +32,45 @@ } } ] + }, + { + "label": "Proxy Configuration", + "properties": [ + { + "widget-type": "textbox", + "label": "Proxy URL", + "name": "proxyUrl" + }, + { + "widget-type": "textbox", + "label": "Username", + "name": "proxyUsername" + }, + { + "widget-type": "password", + "label": "Password", + "name": "proxyPassword" + } + ] + } + ], + "filters":[ + { + "name": "Proxy authentication", + "condition": { + "property": "proxyUrl", + "operator": "exists" + }, + "show": [ + { + "name": "proxyUsername", + "type": "property" + }, + { + "name": "proxyPassword", + "type": "property" + } + ] } ], "outputs": []