diff --git a/docs/SuccessFactors-batchsource.md b/docs/SuccessFactors-batchsource.md
index be73309..e0fade4 100644
--- a/docs/SuccessFactors-batchsource.md
+++ b/docs/SuccessFactors-batchsource.md
@@ -19,8 +19,22 @@ annotating metadata, etc.
**Use Connection:** Whether to use a connection. If a connection is used, you do not need to provide the credentials.
**Connection:** Name of the connection to use. Entity Names information will be provided by the connection.
You also can use the macro function ${conn(connection-name)}.
+**Authentication Type:** Authentication type used to submit request. Supported types are Basic & OAuth 2.0. Default is Basic Authentication.
+* **Basic Authentication**
**SAP SuccessFactors Logon Username (M)**: SAP SuccessFactors Logon Username for user authentication.
**SAP SuccessFactors Logon Password (M)**: SAP SuccessFactors Logon password for user authentication.
+* **OAuth 2.0**
+ **Client ID:** Client ID (API Key) required to generate the token.
+ **Company ID:** Company ID required to generate the token.
+ **Token URL:** Token URL to generate the assertion token.
+ **Assertion Token Type:** Assertion token can be entered or can be created using the required parameters.
+* **Enter Token**
+ **Assertion Token:** Assertion token used to generate the access token.
+* **Create Token**
+ **Private Key:** Private key required to generate the token.
+ **Expire Assertion Token In (Minutes):** Assertion Token will not be valid after the specified time. Default 1440 minutes (24 hours).
+ **User ID:** User ID required to generate the token.
+
**SAP SuccessFactors Base URL (M)**: SAP SuccessFactors Base URL.
## Proxy Configuration
diff --git a/docs/SuccessFactors-connector.md b/docs/SuccessFactors-connector.md
index 85739a9..c0e2b88 100644
--- a/docs/SuccessFactors-connector.md
+++ b/docs/SuccessFactors-connector.md
@@ -10,9 +10,21 @@ Properties
**Description:** Description of the connection.
-**SAP SuccessFactors Logon Username (M)**: SAP SuccessFactors Logon Username for user authentication.
-
-**SAP SuccessFactors Logon Password (M)**: SAP SuccessFactors Logon password for user authentication.
+**Authentication Type:** Authentication type used to submit request. Supported types are Basic & OAuth 2.0. Default is Basic Authentication.
+* **Basic Authentication**
+ **SAP SuccessFactors Logon Username (M)**: SAP SuccessFactors Logon Username for user authentication.
+ **SAP SuccessFactors Logon Password (M)**: SAP SuccessFactors Logon password for user authentication.
+* **OAuth 2.0**
+ **Client ID:** Client ID (API Key) required to generate the token.
+ **Company ID:** Company ID required to generate the token.
+ **Token URL:** Token URL to generate the assertion token.
+ **Assertion Token Type:** Assertion token can be entered or can be created using the required parameters.
+* **Enter Token**
+ **Assertion Token:** Assertion token used to generate the access token.
+* **Create Token**
+ **Private Key:** Private key required to generate the token.
+ **Expire Assertion Token In (Minutes):** Assertion Token will not be valid after the specified time. Default 1440 minutes (24 hours).
+ **User ID:** User ID required to generate the token.
**SAP SuccessFactors Base URL (M)**: SAP SuccessFactors Base URL.
diff --git a/pom.xml b/pom.xml
index b993994..3407080 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,6 +41,7 @@
4.12
2.0.0
4.9.1
+ 4.5.14
2.0.0
2.27.2
2.7.0
@@ -339,7 +340,16 @@
okhttp
${okhttp3.version}
-
+
+ org.apache.httpcomponents
+ httpclient
+ ${httpclient.version}
+
+
+ org.opensaml
+ opensaml
+ 2.6.4
+
com.fasterxml.jackson.core
jackson-databind
diff --git a/src/main/java/io/cdap/plugin/successfactors/common/util/ResourceConstants.java b/src/main/java/io/cdap/plugin/successfactors/common/util/ResourceConstants.java
index debbe80..3177138 100644
--- a/src/main/java/io/cdap/plugin/successfactors/common/util/ResourceConstants.java
+++ b/src/main/java/io/cdap/plugin/successfactors/common/util/ResourceConstants.java
@@ -23,6 +23,7 @@
public enum ResourceConstants {
ERR_MISSING_PARAM_PREFIX(null, "err.missing.param.prefix"),
+ ERR_INVALID_EXPIRE_TIME(null, "err.invalid.expire.time.prefix"),
ERR_MISSING_PARAM_OR_MACRO_ACTION(null, "err.missing.param.or.macro.action"),
ERR_INVALID_BASE_URL(null, "err.invalid.base.url"),
ERR_FEATURE_NOT_SUPPORTED("CDF_SAP_ODATA_01500", "err.feature.not.supported"),
diff --git a/src/main/java/io/cdap/plugin/successfactors/common/util/SuccessFactorsAccessToken.java b/src/main/java/io/cdap/plugin/successfactors/common/util/SuccessFactorsAccessToken.java
new file mode 100644
index 0000000..79cb681
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/successfactors/common/util/SuccessFactorsAccessToken.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright © 2025 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.cdap.plugin.successfactors.common.util;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import io.cdap.plugin.successfactors.connector.SuccessFactorsConnectorConfig;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.joda.time.DateTime;
+import org.opensaml.Configuration;
+import org.opensaml.DefaultBootstrap;
+import org.opensaml.common.SAMLVersion;
+import org.opensaml.saml1.core.NameIdentifier;
+import org.opensaml.saml2.core.Assertion;
+import org.opensaml.saml2.core.Attribute;
+import org.opensaml.saml2.core.AttributeStatement;
+import org.opensaml.saml2.core.AttributeValue;
+import org.opensaml.saml2.core.Audience;
+import org.opensaml.saml2.core.AudienceRestriction;
+import org.opensaml.saml2.core.AuthnContext;
+import org.opensaml.saml2.core.AuthnContextClassRef;
+import org.opensaml.saml2.core.AuthnStatement;
+import org.opensaml.saml2.core.Conditions;
+import org.opensaml.saml2.core.Issuer;
+import org.opensaml.saml2.core.NameID;
+import org.opensaml.saml2.core.Subject;
+import org.opensaml.saml2.core.SubjectConfirmation;
+import org.opensaml.saml2.core.SubjectConfirmationData;
+import org.opensaml.saml2.core.impl.AssertionMarshaller;
+import org.opensaml.xml.ConfigurationException;
+import org.opensaml.xml.Namespace;
+import org.opensaml.xml.XMLObjectBuilder;
+import org.opensaml.xml.io.MarshallingException;
+import org.opensaml.xml.schema.XSString;
+import org.opensaml.xml.schema.impl.XSStringBuilder;
+import org.opensaml.xml.security.SecurityConfiguration;
+import org.opensaml.xml.security.SecurityException;
+import org.opensaml.xml.security.SecurityHelper;
+import org.opensaml.xml.security.x509.BasicX509Credential;
+import org.opensaml.xml.signature.Signature;
+import org.opensaml.xml.signature.SignatureConstants;
+import org.opensaml.xml.signature.SignatureException;
+import org.opensaml.xml.signature.Signer;
+import org.opensaml.xml.util.XMLHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.List;
+import java.util.UUID;
+
+import javax.xml.namespace.QName;
+
+
+/**
+ * AccessToken class
+ */
+public class SuccessFactorsAccessToken {
+ private static final Logger LOG = LoggerFactory.getLogger(SuccessFactorsAccessToken.class);
+ private final SuccessFactorsConnectorConfig config;
+ private final Gson gson = new Gson();
+
+
+ public SuccessFactorsAccessToken(SuccessFactorsConnectorConfig config) {
+ this.config = config;
+ }
+
+ /**
+ * Generates a signed SAML assertion for authentication purposes.
+ *
+ * @param clientId The client ID associated with the application.
+ * @param username The username of the user for whom the assertion is generated.
+ * @param tokenUrl The URL for obtaining the authentication token.
+ * @param privateKeyString The private key used for signing the assertion.
+ * @param expireInMinutes The validity period of the assertion in minutes.
+ * @param userUserNameAsUserId A boolean indicating whether to use the username as the User ID in the assertion.
+ * @return The signed SAML assertion as a string.
+ * @throws Exception If an error occurs during the generation or signing of the SAML assertion.
+ */
+ public static String generateSignedSAMLAssertion(String clientId, String username, String tokenUrl,
+ String privateKeyString, int expireInMinutes,
+ boolean userUserNameAsUserId) {
+
+ Assertion unsignedAssertion = buildDefaultAssertion(clientId, username, tokenUrl, expireInMinutes,
+ userUserNameAsUserId);
+ PrivateKey privateKey = generatePrivateKey(privateKeyString);
+ Assertion assertion = sign(unsignedAssertion, privateKey);
+ String signedAssertion = getSAMLAssertionString(assertion);
+
+ return signedAssertion;
+ }
+
+ /**
+ * Builds a default SAML assertion with specified parameters for authentication purposes.
+ *
+ * @param clientId The client ID associated with the application.
+ * @param userId The user ID for whom the assertion is generated.
+ * @param tokenUrl The URL for obtaining the authentication token.
+ * @param expireInMinutes The validity period of the assertion in minutes.
+ * @param userUserNameAsUserId A boolean indicating whether to use the username as the User ID in the assertion.
+ * @return The constructed SAML assertion.
+ * @throws RuntimeException if an error occurs during the construction of the SAML assertion.
+ */
+ private static Assertion buildDefaultAssertion(String clientId, String userId, String tokenUrl, int expireInMinutes,
+ boolean userUserNameAsUserId) {
+ try {
+ DateTime currentTime = new DateTime();
+ DefaultBootstrap.bootstrap();
+
+ // Create the assertion and set Id, namespace etc.
+ Assertion assertion = create(Assertion.class, Assertion.DEFAULT_ELEMENT_NAME);
+ assertion.setIssueInstant(currentTime);
+ assertion.setID(UUID.randomUUID().toString());
+ assertion.setVersion(SAMLVersion.VERSION_20);
+ Namespace xsNS = new Namespace("http://www.w3.org/2001/XMLSchema", "xs");
+ assertion.addNamespace(xsNS);
+ Namespace xsiNS = new Namespace("http://www.w3.org/2001/XMLSchema-instance", "xsi");
+ assertion.addNamespace(xsiNS);
+
+ Issuer issuer = create(Issuer.class, Issuer.DEFAULT_ELEMENT_NAME);
+ issuer.setValue("www.successfactors.com");
+ assertion.setIssuer(issuer);
+
+ // Create the subject and add it to assertion
+ Subject subject = create(Subject.class, Subject.DEFAULT_ELEMENT_NAME);
+ NameID nameID = create(NameID.class, NameID.DEFAULT_ELEMENT_NAME);
+ nameID.setValue(userId);
+ nameID.setFormat(NameIdentifier.UNSPECIFIED);
+ subject.setNameID(nameID);
+ SubjectConfirmation subjectConfirmation = create(SubjectConfirmation.class,
+ SubjectConfirmation.DEFAULT_ELEMENT_NAME);
+ subjectConfirmation.setMethod("urn:oasis:names:tc:SAML:2.0:cm:bearer");
+ SubjectConfirmationData sconfData = create(SubjectConfirmationData.class,
+ SubjectConfirmationData.DEFAULT_ELEMENT_NAME);
+ sconfData.setNotOnOrAfter(currentTime.plusMinutes(expireInMinutes));
+ sconfData.setRecipient(tokenUrl);
+ subjectConfirmation.setSubjectConfirmationData(sconfData);
+ subject.getSubjectConfirmations().add(subjectConfirmation);
+ assertion.setSubject(subject);
+
+ // Create the Conditions
+ Conditions conditions = buildConditions(currentTime, expireInMinutes);
+
+ AudienceRestriction audienceRestriction = create(AudienceRestriction.class,
+ AudienceRestriction.DEFAULT_ELEMENT_NAME);
+ Audience audience = create(Audience.class, Audience.DEFAULT_ELEMENT_NAME);
+ audience.setAudienceURI("www.successfactors.com");
+ List audienceList = audienceRestriction.getAudiences();
+ audienceList.add(audience);
+ List audienceRestrictions = conditions.getAudienceRestrictions();
+ audienceRestrictions.add(audienceRestriction);
+ assertion.setConditions(conditions);
+
+ // Create the AuthnStatement and add it to assertion
+ AuthnStatement authnStatement = create(AuthnStatement.class, AuthnStatement.DEFAULT_ELEMENT_NAME);
+ authnStatement.setAuthnInstant(currentTime);
+ authnStatement.setSessionIndex(UUID.randomUUID().toString());
+ AuthnContext authContext = create(AuthnContext.class, AuthnContext.DEFAULT_ELEMENT_NAME);
+ AuthnContextClassRef authnContextClassRef = create(AuthnContextClassRef.class,
+ AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
+ authnContextClassRef.setAuthnContextClassRef(AuthnContext.PPT_AUTHN_CTX);
+ authContext.setAuthnContextClassRef(authnContextClassRef);
+ authnStatement.setAuthnContext(authContext);
+ assertion.getAuthnStatements().add(authnStatement);
+
+ // Create the attribute statement
+ AttributeStatement attributeStatement = create(AttributeStatement.class,
+ AttributeStatement.DEFAULT_ELEMENT_NAME);
+ Attribute apiKeyAttribute = createAttribute("api_key", clientId);
+ attributeStatement.getAttributes().add(apiKeyAttribute);
+ assertion.getAttributeStatements().add(attributeStatement);
+
+ // Set user_username as true while using username as userId
+ if (userUserNameAsUserId) {
+ AttributeStatement useUserNameAsUserIdStatement = create(AttributeStatement.class,
+ AttributeStatement.DEFAULT_ELEMENT_NAME);
+ Attribute useUserNameKeyAttribute = createAttribute("use_username", "true");
+ useUserNameAsUserIdStatement.getAttributes().add(useUserNameKeyAttribute);
+ assertion.getAttributeStatements().add(useUserNameAsUserIdStatement);
+ }
+
+ return assertion;
+ } catch (ConfigurationException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * helper method to create open saml objects.
+ * @param cls class type
+ * @param qname qualified name
+ * @param class type
+ * @return the saml object
+ */
+ @SuppressWarnings("unchecked")
+ public static T create(Class cls, QName qname) {
+ return (T) ((XMLObjectBuilder) Configuration.getBuilderFactory().getBuilder(qname)).buildObject(qname);
+ }
+
+ private static Attribute createAttribute(String name, String value) {
+ Attribute result = create(Attribute.class, Attribute.DEFAULT_ELEMENT_NAME);
+ result.setName(name);
+ XSStringBuilder stringBuilder = (XSStringBuilder) Configuration.getBuilderFactory()
+ .getBuilder(XSString.TYPE_NAME);
+ XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME);
+ stringValue.setValue(value);
+ result.getAttributeValues().add(stringValue);
+ return result;
+ }
+
+ private static Conditions buildConditions(DateTime currentTime, int expireInMinutes) {
+ Conditions conditions = create(Conditions.class, Conditions.DEFAULT_ELEMENT_NAME);
+ conditions.setNotBefore(currentTime.minusMinutes(10));
+ conditions.setNotOnOrAfter(currentTime.plusMinutes(expireInMinutes));
+ return conditions;
+ }
+
+ private static String getSAMLAssertionString(Assertion assertion) {
+ AssertionMarshaller marshaller = new AssertionMarshaller();
+ Element element = null;
+ try {
+ element = marshaller.marshall(assertion);
+ } catch (MarshallingException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ String unencodedSAMLAssertion = XMLHelper.nodeToString(element);
+
+ Base64 base64 = new Base64();
+ try {
+ return base64.encodeToString(unencodedSAMLAssertion.getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Signs a SAML assertion using the provided private key.
+ *
+ * @param assertion The unsigned SAML assertion to be signed.
+ * @param privateKey The private key used for signing the assertion.
+ * @return The signed SAML assertion.
+ * @throws Exception If an error occurs during the signing process.
+ * - If the SAML assertion is already signed.
+ * - If an invalid X.509 private key is provided.
+ * - If there is a failure in signing the SAML2 assertion.
+ */
+ private static Assertion sign(Assertion assertion, PrivateKey privateKey) {
+ BasicX509Credential credential = new BasicX509Credential();
+ credential.setPrivateKey(privateKey);
+
+ if (assertion.getSignature() != null) {
+ throw new RuntimeException("SAML assertion is already signed");
+ }
+
+ if (privateKey == null) {
+ throw new RuntimeException("Invalid X.509 private key");
+ }
+
+ try {
+ Signature signature = (Signature) Configuration.getBuilderFactory()
+ .getBuilder(Signature.DEFAULT_ELEMENT_NAME).buildObject(Signature.DEFAULT_ELEMENT_NAME);
+ signature.setSigningCredential(credential);
+ SecurityConfiguration secConfig = Configuration.getGlobalSecurityConfiguration();
+ String keyInfoGeneratorProfile = null; // "XMLSignature";
+ SecurityHelper.prepareSignatureParams(signature, credential, secConfig, keyInfoGeneratorProfile);
+
+ // Support sha256 signing algorithm for external oauth saml assertion
+ signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
+
+ assertion.setSignature(signature);
+ Configuration.getMarshallerFactory().getMarshaller(assertion).marshall(assertion);
+ Signer.signObject(signature);
+ } catch (MarshallingException | SecurityException | SignatureException e) {
+ throw new RuntimeException("Failure in signing the SAML2 assertion", e);
+ }
+ return assertion;
+ }
+
+ private static PrivateKey generatePrivateKey(String privateKeyString) {
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+
+ // Decode the base64-encoded private key string
+ String pk2 = new String(Base64.decodeBase64(privateKeyString), "UTF-8");
+
+ // Extract the actual private key string if it is in a format like "privateKey###additionalInfo"
+ String[] strs = pk2.split("###");
+ if (strs.length == 2) {
+ privateKeyString = strs[0];
+ }
+
+ // Generate the private key from the decoded key string
+ PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString));
+ return keyFactory.generatePrivate(privateKeySpec);
+ } catch (NoSuchAlgorithmException | UnsupportedEncodingException | InvalidKeySpecException e) {
+ // Throw a runtime exception if an error occurs during the private key generation process
+ throw new RuntimeException("Error generating private key", e);
+ }
+ }
+
+ public String getAssertionToken() {
+
+ /**
+ * Below code is to produce signed assertion via code directly using provided
+ * input
+ */
+ String tokenUrl = config.getTokenURL(), clientId = config.getClientId(),
+ privateKey = config.getPrivateKey(), userId = config.getUserId();
+ boolean useUserNameAsUserId = false;
+ if (tokenUrl != null && clientId != null && privateKey != null && userId != null) {
+ LOG.info("All properties are set, generating the SAML Assertion...");
+
+ String signedSAMLAssertion = generateSignedSAMLAssertion(clientId, userId, tokenUrl, privateKey,
+ config.getExpireInMinutes(), useUserNameAsUserId);
+ LOG.info("Signed SAML Assertion is generated");
+ return signedSAMLAssertion;
+ }
+ return null;
+ }
+
+ public String getAccessToken(String assertionToken) throws IOException {
+ HttpClient client = HttpClientBuilder.create().build();
+
+ // Build POST request
+ HttpPost request = new HttpPost(URI.create(config.getTokenURL()));
+
+ // Set headers
+ request.setHeader("Authorization", "none");
+ request.setHeader("Content-Type", "application/x-www-form-urlencoded");
+
+ // Build request body
+ StringBuilder body = new StringBuilder();
+ body.append("client_id=").append(config.getClientId());
+ body.append("&company_id=").append(config.getCompanyId());
+ body.append("&grant_type=").append("urn:ietf:params:oauth:grant-type:saml2-bearer");
+ body.append("&assertion=").append(assertionToken);
+
+ // Set request entity
+ request.setEntity(new StringEntity(body.toString()));
+
+ // Execute request and get response
+ HttpResponse response = client.execute(request);
+ String accessToken = null;
+ JsonObject jsonObject = null;
+
+ // Read response body
+ if (response.getEntity() != null) {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))) {
+ jsonObject = gson.fromJson(reader, JsonObject.class);
+
+ // Check if "access_token" is present in the JSON response
+ if (jsonObject != null && jsonObject.has("access_token")) {
+ accessToken = jsonObject.get("access_token").getAsString();
+ }
+ }
+ }
+ return accessToken;
+ }
+}
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 c377536..d759aca 100644
--- a/src/main/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnector.java
+++ b/src/main/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnector.java
@@ -101,7 +101,7 @@ public SuccessFactorsConnector(SuccessFactorsConnectorConfig config) {
@Override
public void test(ConnectorContext connectorContext) throws ValidationException {
FailureCollector collector = connectorContext.getFailureCollector();
- config.validateBasicCredentials(collector);
+ config.validateAuthCredentials(collector);
config.validateConnection(collector);
}
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 57377f3..9d35b46 100644
--- a/src/main/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnectorConfig.java
+++ b/src/main/java/io/cdap/plugin/successfactors/connector/SuccessFactorsConnectorConfig.java
@@ -15,6 +15,7 @@
*/
package io.cdap.plugin.successfactors.connector;
+import com.google.common.base.Strings;
import io.cdap.cdap.api.annotation.Description;
import io.cdap.cdap.api.annotation.Macro;
import io.cdap.cdap.api.annotation.Name;
@@ -40,30 +41,96 @@
* SuccessFactorsConnectorConfig Class
*/
public class SuccessFactorsConnectorConfig extends PluginConfig {
-
+ public static final String PROPERTY_AUTH_TYPE = "authType";
+ public static final String ASSERTION_TOKEN_TYPE = "assertionTokenType";
+ public static final String BASIC_AUTH = "basicAuth";
+ public static final String OAUTH2 = "oAuth2";
+ public static final String ENTER_TOKEN = "enterToken";
+ public static final String CREATE_TOKEN = "createToken";
public static final String BASE_URL = "baseURL";
public static final String UNAME = "username";
public static final String PASSWORD = "password";
+ public static final String TOKEN_URL = "tokenURL";
+ public static final String CLIENT_ID = "clientId";
+ public static final String PRIVATE_KEY = "privateKey";
+ public static final String EXPIRE_IN_MINUTES = "expireInMinutes";
+ public static final Integer DEFAULT_EXPIRE_IN_MINUTES = 24 * 60;
+ public static final String USER_ID = "userId";
+ public static final String ASSERTION_TOKEN = "assertionToken";
+ public static final String COMPANY_ID = "companyId";
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";
private static final String SAP_SUCCESSFACTORS_PASSWORD = "SAP SuccessFactors Password";
private static final String SAP_SUCCESSFACTORS_BASE_URL = "SAP SuccessFactors Base URL";
private static final Logger LOG = LoggerFactory.getLogger(SuccessFactorsConnectorConfig.class);
+ @Nullable
+ @Name(PROPERTY_AUTH_TYPE)
+ @Description("Type of authentication used to submit request. OAuth 2.0, Basic Authentication types are available.")
+ protected String authType;
+
+ @Nullable
+ @Name(ASSERTION_TOKEN_TYPE)
+ @Description("Assertion token can be entered or can be created using the required parameters.")
+ protected String assertionTokenType;
+
+ @Nullable
@Name(UNAME)
@Macro
@Description("SAP SuccessFactors Username for user authentication.")
private final String username;
+ @Nullable
@Name(PASSWORD)
@Macro
@Description("SAP SuccessFactors password for user authentication.")
private final String password;
+ @Nullable
+ @Name(TOKEN_URL)
+ @Macro
+ @Description("Token URL to generate the assertion token.")
+ private final String tokenURL;
+
+ @Nullable
+ @Name(CLIENT_ID)
+ @Macro
+ @Description("Client Id to generate the token.")
+ private final String clientId;
+
+ @Nullable
+ @Name(PRIVATE_KEY)
+ @Macro
+ @Description("Private key to generate the token.")
+ private final String privateKey;
+
+ @Nullable
+ @Name(EXPIRE_IN_MINUTES)
+ @Macro
+ @Description("Assertion Token will not be valid after the specified time. Default 1440 minutes (24 hours).")
+ private final Integer expireInMinutes;
+
+ @Nullable
+ @Name(USER_ID)
+ @Macro
+ @Description("User Id to generate the token.")
+ private final String userId;
+
+ @Nullable
+ @Name(ASSERTION_TOKEN)
+ @Macro
+ @Description("Assertion token used to generate the access token.")
+ private final String assertionToken;
+
+ @Nullable
+ @Name(COMPANY_ID)
+ @Macro
+ @Description("Company Id to generate the token.")
+ private final String companyId;
+
@Macro
@Name(BASE_URL)
@Description("SuccessFactors Base URL.")
@@ -86,11 +153,27 @@ public class SuccessFactorsConnectorConfig extends PluginConfig {
@Description("Proxy password.")
@Macro
private String proxyPassword;
-
- public SuccessFactorsConnectorConfig(String username, String password, String baseURL, String proxyUrl,
- String proxyUsername, String proxyPassword) {
+
+ public SuccessFactorsConnectorConfig(@Nullable String username, @Nullable String password,
+ String tokenURL,
+ @Nullable String clientId, @Nullable String privateKey,
+ @Nullable Integer expireInMinutes, @Nullable String userId,
+ @Nullable String companyId, String baseURL, String authType,
+ String assertionTokenType, @Nullable String samlUsername,
+ @Nullable String assertionToken,
+ @Nullable String proxyUrl,
+ @Nullable String proxyUsername, @Nullable String proxyPassword) {
this.username = username;
this.password = password;
+ this.tokenURL = tokenURL;
+ this.clientId = clientId;
+ this.privateKey = privateKey;
+ this.expireInMinutes = expireInMinutes;
+ this.userId = userId;
+ this.companyId = companyId;
+ this.authType = authType;
+ this.assertionTokenType = assertionTokenType;
+ this.assertionToken = assertionToken;
this.baseURL = baseURL;
this.proxyUrl = proxyUrl;
this.proxyUsername = proxyUsername;
@@ -117,25 +200,112 @@ public String getPassword() {
return password;
}
+ public String getAuthType() {
+ // Default to basic auth if authType is not set for backward compatibility
+ return Strings.isNullOrEmpty(authType) ? SuccessFactorsConnectorConfig.BASIC_AUTH : authType;
+ }
+
+ @Nullable
+ public String getTokenURL() {
+ return tokenURL;
+ }
+
+ @Nullable
+ public String getCompanyId() {
+ return companyId;
+ }
+
+ @Nullable
+ public String getAssertionTokenType() {
+ return assertionTokenType;
+ }
+
+ @Nullable
+ public String getClientId() {
+ return clientId;
+ }
+
+ @Nullable
+ public String getAssertionToken() {
+ return assertionToken;
+ }
+
+ @Nullable
+ public String getPrivateKey() {
+ return privateKey;
+ }
+
+ public Integer getExpireInMinutes() {
+ return expireInMinutes == null ? DEFAULT_EXPIRE_IN_MINUTES : expireInMinutes;
+ }
+
+ @Nullable
+ public String getUserId() {
+ return userId;
+ }
+
public String getBaseURL() {
return baseURL;
}
- public void validateBasicCredentials(FailureCollector failureCollector) {
+ public void validateAuthCredentials(FailureCollector failureCollector) {
+
+ if (BASIC_AUTH.equals(getAuthType())) {
+ if (!containsMacro(UNAME) && Strings.isNullOrEmpty(getUsername())) {
+ String errMsg = ResourceConstants.ERR_MISSING_PARAM_PREFIX.getMsgForKey(SAP_SUCCESSFACTORS_USERNAME);
+ failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(UNAME);
+ }
+ if (!containsMacro(PASSWORD) && Strings.isNullOrEmpty(getPassword())) {
+ String errMsg = ResourceConstants.ERR_MISSING_PARAM_PREFIX.getMsgForKey(SAP_SUCCESSFACTORS_PASSWORD);
+ failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(PASSWORD);
+ }
+ } else {
+ if (!containsMacro(CLIENT_ID) && Strings.isNullOrEmpty(getClientId())) {
+ String errMsg = ResourceConstants.ERR_MISSING_PARAM_PREFIX.getMsgForKey(CLIENT_ID);
+ failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(CLIENT_ID);
+ }
+ if (!containsMacro(COMPANY_ID) && Strings.isNullOrEmpty(getCompanyId())) {
+ String errMsg = ResourceConstants.ERR_MISSING_PARAM_PREFIX.getMsgForKey(COMPANY_ID);
+ failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(COMPANY_ID);
+ }
+ if (!containsMacro(TOKEN_URL) && Strings.isNullOrEmpty(getTokenURL())) {
+ String errMsg = ResourceConstants.ERR_MISSING_PARAM_PREFIX.getMsgForKey(TOKEN_URL);
+ failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(TOKEN_URL);
+ }
+ if (!containsMacro(TOKEN_URL) && !Strings.isNullOrEmpty(getTokenURL()) && HttpUrl.parse(getTokenURL()) == null) {
+ String errMsg = ResourceConstants.ERR_INVALID_BASE_URL.getMsgForKey(TOKEN_URL);
+ failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(TOKEN_URL);
+ }
+
+ if (ENTER_TOKEN.equals(assertionTokenType)) {
+ if (!containsMacro(ASSERTION_TOKEN) && Strings.isNullOrEmpty(getAssertionToken())) {
+ String errMsg = ResourceConstants.ERR_MISSING_PARAM_PREFIX.getMsgForKey(ASSERTION_TOKEN);
+ failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(ASSERTION_TOKEN);
+ }
+ }
+
+ if (CREATE_TOKEN.equals(assertionTokenType)) {
+ if (!containsMacro(PRIVATE_KEY) && Strings.isNullOrEmpty(getPrivateKey())) {
+ String errMsg = ResourceConstants.ERR_MISSING_PARAM_PREFIX.getMsgForKey(PRIVATE_KEY);
+ failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(PRIVATE_KEY);
+ }
+ if (!containsMacro(EXPIRE_IN_MINUTES) && getExpireInMinutes() <= 0) {
+ String errMsg = ResourceConstants.ERR_INVALID_EXPIRE_TIME.getMsgForKey(EXPIRE_IN_MINUTES);
+ failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(EXPIRE_IN_MINUTES);
+ }
+ if (!containsMacro(USER_ID) && Strings.isNullOrEmpty(getUserId())) {
+ String errMsg = ResourceConstants.ERR_MISSING_PARAM_PREFIX.getMsgForKey(USER_ID);
+ failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(USER_ID);
+ }
+ }
- if (SuccessFactorsUtil.isNullOrEmpty(getUsername()) && !containsMacro(UNAME)) {
- String errMsg = ResourceConstants.ERR_MISSING_PARAM_PREFIX.getMsgForKey(SAP_SUCCESSFACTORS_USERNAME);
- failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(UNAME);
- }
- if (SuccessFactorsUtil.isNullOrEmpty(getPassword()) && !containsMacro(PASSWORD)) {
- String errMsg = ResourceConstants.ERR_MISSING_PARAM_PREFIX.getMsgForKey(SAP_SUCCESSFACTORS_PASSWORD);
- failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(PASSWORD);
}
- if (SuccessFactorsUtil.isNullOrEmpty(getBaseURL()) && !containsMacro(BASE_URL)) {
+
+ if (!containsMacro(BASE_URL) && Strings.isNullOrEmpty(getBaseURL())) {
String errMsg = ResourceConstants.ERR_MISSING_PARAM_PREFIX.getMsgForKey(SAP_SUCCESSFACTORS_BASE_URL);
failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(BASE_URL);
}
- if (SuccessFactorsUtil.isNotNullOrEmpty(getBaseURL()) && !containsMacro(BASE_URL)) {
+ if (!containsMacro(BASE_URL) && SuccessFactorsUtil.isNotNullOrEmpty(getBaseURL())) {
if (HttpUrl.parse(getBaseURL()) == null) {
String errMsg = ResourceConstants.ERR_INVALID_BASE_URL.getMsgForKey(SAP_SUCCESSFACTORS_BASE_URL);
failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(BASE_URL);
@@ -156,7 +326,7 @@ public void validateConnection(FailureCollector collector) {
} catch (TransportException e) {
LOG.error("Unable to fetch the response", e);
collector.addFailure("Unable to call SuccessFactorsEntity",
- "Please check the values for basic and proxy parameters if proxy exists.");
+ "Please check the values for connection and proxy parameters if proxy exists.");
return;
}
if (responseContainer.getHttpStatusCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
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 2e5a49c..6a736ee 100644
--- a/src/main/java/io/cdap/plugin/successfactors/source/SuccessFactorsSource.java
+++ b/src/main/java/io/cdap/plugin/successfactors/source/SuccessFactorsSource.java
@@ -15,6 +15,7 @@
*/
package io.cdap.plugin.successfactors.source;
+import com.google.common.base.Strings;
import com.google.gson.Gson;
import io.cdap.cdap.api.annotation.Description;
import io.cdap.cdap.api.annotation.Metadata;
@@ -138,6 +139,9 @@ private Schema getOutputSchema(FailureCollector failureCollector) {
errorMsg = ResourceConstants.ERR_ODATA_SERVICE_CALL.getMsgForKeyWithCode(errorMsg);
if (SuccessFactorsUtil.isNullOrEmpty(config.getConnection().getProxyUrl())) {
failureCollector.addFailure(errorMsg, null).withConfigProperty(SuccessFactorsPluginConfig.BASE_URL);
+ if (!Strings.isNullOrEmpty(config.getConnection().getTokenURL())) {
+ failureCollector.addFailure(errorMsg, null).withConfigProperty(SuccessFactorsPluginConfig.TOKEN_URL);
+ }
} else {
failureCollector.addFailure(errorMsg,
"Unable to connect to successFactors. Please check the basic and proxy connection parameters")
@@ -164,8 +168,21 @@ private void attachFieldWithError(SuccessFactorsServiceException ose, FailureCol
errMsg = ResourceConstants.ERR_ODATA_ENTITY_FAILURE.getMsgForKeyWithCode(errMsg);
switch (ose.getErrorCode()) {
case HttpURLConnection.HTTP_UNAUTHORIZED:
- failureCollector.addFailure(errMsg, null).withConfigProperty(SuccessFactorsPluginConfig.UNAME);
- failureCollector.addFailure(errMsg, null).withConfigProperty(SuccessFactorsPluginConfig.PASSWORD);
+ if (SuccessFactorsConnectorConfig.BASIC_AUTH.equals(config.getConnection().getAuthType())) {
+ failureCollector.addFailure(errMsg, null).withConfigProperty(SuccessFactorsPluginConfig.UNAME);
+ failureCollector.addFailure(errMsg, null).withConfigProperty(SuccessFactorsPluginConfig.PASSWORD);
+ } else {
+ failureCollector.addFailure(errMsg, null).withConfigProperty(SuccessFactorsConnectorConfig.COMPANY_ID);
+ failureCollector.addFailure(errMsg, null).withConfigProperty(SuccessFactorsConnectorConfig.CLIENT_ID);
+ if (SuccessFactorsConnectorConfig.ENTER_TOKEN.equals(config.getConnection().getAssertionTokenType())) {
+ failureCollector.addFailure(errMsg, null).
+ withConfigProperty(SuccessFactorsConnectorConfig.ASSERTION_TOKEN);
+ } else {
+ failureCollector.addFailure(errMsg, null).withConfigProperty(SuccessFactorsConnectorConfig.PRIVATE_KEY);
+ failureCollector.addFailure(errMsg, null).withConfigProperty(SuccessFactorsConnectorConfig.USER_ID);
+ failureCollector.addFailure(errMsg, null).withConfigProperty(SuccessFactorsConnectorConfig.TOKEN_URL);
+ }
+ }
break;
case HttpURLConnection.HTTP_FORBIDDEN:
case ExceptionParser.NO_VERSION_FOUND:
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 3594323..1772fee 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
@@ -40,6 +40,7 @@
*/
public class SuccessFactorsPluginConfig extends PluginConfig {
public static final String BASE_URL = "baseURL";
+ public static final String TOKEN_URL = "tokenURL";
public static final String ENTITY_NAME = "entityName";
public static final String UNAME = "username";
public static final String PASSWORD = "password";
@@ -170,6 +171,16 @@ public SuccessFactorsPluginConfig(String referenceName,
@Nullable String proxyUrl,
@Nullable String proxyPassword,
@Nullable String proxyUsername,
+ @Nullable String tokenURL,
+ @Nullable String clientId,
+ @Nullable String privateKey,
+ @Nullable Integer expireInMinutes,
+ @Nullable String userId,
+ @Nullable String samlUsername,
+ @Nullable String assertionToken,
+ @Nullable String companyId,
+ String authType,
+ String assertionTokenType,
@Nullable String filterOption,
@Nullable String selectOption,
@Nullable String expandOption,
@@ -179,8 +190,9 @@ public SuccessFactorsPluginConfig(String referenceName,
@Nullable Integer maxRetryDuration,
@Nullable Integer retryMultiplier,
@Nullable Integer maxRetryCount) {
- this.connection = new SuccessFactorsConnectorConfig(username, password, baseURL, proxyUrl, proxyPassword,
- proxyUsername);
+ this.connection = new SuccessFactorsConnectorConfig(username, password, tokenURL, clientId, privateKey,
+ expireInMinutes, userId, companyId, baseURL, authType, assertionTokenType, samlUsername, assertionToken,
+ proxyUrl, proxyUsername, proxyPassword);
this.referenceName = referenceName;
this.entityName = entityName;
this.associateEntityName = associateEntityName;
@@ -194,6 +206,7 @@ public SuccessFactorsPluginConfig(String referenceName,
this.retryMultiplier = retryMultiplier;
this.maxRetryCount = maxRetryCount;
}
+
@Nullable
public SuccessFactorsConnectorConfig getConnection() {
return connection;
@@ -277,7 +290,14 @@ public boolean isSchemaBuildRequired() {
return !(containsMacro(UNAME) || containsMacro(PASSWORD) || containsMacro(BASE_URL) || containsMacro(ENTITY_NAME)
|| containsMacro(SuccessFactorsConnectorConfig.PROPERTY_PROXY_URL)
|| containsMacro(SuccessFactorsConnectorConfig.PROPERTY_PROXY_USERNAME)
- || containsMacro(SuccessFactorsConnectorConfig.PROPERTY_PROXY_PASSWORD));
+ || containsMacro(SuccessFactorsConnectorConfig.PROPERTY_PROXY_PASSWORD)
+ || containsMacro(SuccessFactorsConnectorConfig.TOKEN_URL)
+ || containsMacro(SuccessFactorsConnectorConfig.CLIENT_ID)
+ || containsMacro(SuccessFactorsConnectorConfig.PRIVATE_KEY)
+ || containsMacro(SuccessFactorsConnectorConfig.EXPIRE_IN_MINUTES)
+ || containsMacro(SuccessFactorsConnectorConfig.USER_ID)
+ || containsMacro(SuccessFactorsConnectorConfig.COMPANY_ID)
+ || containsMacro(SuccessFactorsConnectorConfig.ASSERTION_TOKEN));
}
/**
@@ -311,7 +331,7 @@ public void validatePluginParameters(FailureCollector failureCollector) {
private void validateMandatoryParameters(FailureCollector failureCollector) {
IdUtils.validateReferenceName(getReferenceName(), failureCollector);
- if (SuccessFactorsUtil.isNullOrEmpty(getEntityName()) && !containsMacro(ENTITY_NAME)) {
+ if (!containsMacro(ENTITY_NAME) && SuccessFactorsUtil.isNullOrEmpty(getEntityName())) {
String errMsg = ResourceConstants.ERR_MISSING_PARAM_PREFIX.getMsgForKey(SAP_SUCCESSFACTORS_ENTITY_NAME);
failureCollector.addFailure(errMsg, COMMON_ACTION).withConfigProperty(ENTITY_NAME);
}
@@ -324,7 +344,7 @@ private void validateMandatoryParameters(FailureCollector failureCollector) {
*/
private void validateBasicCredentials(FailureCollector failureCollector) {
if (connection != null) {
- connection.validateBasicCredentials(failureCollector);
+ connection.validateAuthCredentials(failureCollector);
}
}
@@ -335,14 +355,14 @@ private void validateBasicCredentials(FailureCollector failureCollector) {
* @param failureCollector {@code FailureCollector}
*/
private void validateEntityParameter(FailureCollector failureCollector) {
- if (SuccessFactorsUtil.isNotNullOrEmpty(getEntityName()) && !containsMacro(getEntityName())) {
+ if (!containsMacro(getEntityName()) && SuccessFactorsUtil.isNotNullOrEmpty(getEntityName())) {
if (PATTERN.matcher(getEntityName()).find()) {
failureCollector.addFailure(ResourceConstants.ERR_FEATURE_NOT_SUPPORTED.getMsgForKey(), null)
.withConfigProperty(ENTITY_NAME);
}
}
if (SuccessFactorsUtil.isNotNullOrEmpty(associateEntityName)) {
- if (SuccessFactorsUtil.isNullOrEmpty(getExpandOption()) && !containsMacro(EXPAND_OPTION)) {
+ if (!containsMacro(EXPAND_OPTION) && SuccessFactorsUtil.isNullOrEmpty(getExpandOption())) {
failureCollector.addFailure(ResourceConstants.ERR_INVALID_ENTITY_CALL.getMsgForKey(), null)
.withConfigProperty(ASSOCIATED_ENTITY_NAME);
}
@@ -401,6 +421,16 @@ public static class Builder {
private String expandOption;
private String paginationType;
private String additionalQueryParameters;
+ private String tokenURL;
+ private String clientId;
+ private String privateKey;
+ private Integer expireInMinutes;
+ private String userId;
+ private String samlUsername;
+ private String assertionToken;
+ private String companyId;
+ private String authType;
+ private String assertionTokenType;
private String proxyUrl;
private String proxyUsername;
private String proxyPassword;
@@ -467,11 +497,51 @@ public Builder expandOption(@Nullable String expandOption) {
return this;
}
+ public Builder authType(@Nullable String authType) {
+ this.authType = Strings.isNullOrEmpty(authType) ? SuccessFactorsConnectorConfig.BASIC_AUTH : authType;
+ return this;
+ }
+
+ public Builder setTokenURL(@Nullable String tokenURL) {
+ this.tokenURL = tokenURL;
+ return this;
+ }
+
+ public Builder setClientId(@Nullable String clientId) {
+ this.clientId = clientId;
+ return this;
+ }
+
+ public Builder setPrivateKey(@Nullable String privateKey) {
+ this.privateKey = privateKey;
+ return this;
+ }
+
+ public Builder setExpireInMinutes(@Nullable Integer expireInMinutes) {
+ this.expireInMinutes = expireInMinutes;
+ return this;
+ }
+
+ public Builder setUserId(@Nullable String userId) {
+ this.userId = userId;
+ return this;
+ }
+
public Builder paginationType(@Nullable String paginationType) {
this.paginationType = paginationType;
return this;
}
+ public Builder assertionTokenType(@Nullable String assertionTokenType) {
+ this.assertionTokenType = assertionTokenType;
+ return this;
+ }
+
+ public Builder assertionToken(@Nullable String assertionToken) {
+ this.assertionToken = assertionToken;
+ return this;
+ }
+
public Builder additionalQueryParameters(@Nullable String additionalQueryParameters) {
this.additionalQueryParameters = additionalQueryParameters;
return this;
@@ -495,11 +565,12 @@ public Builder setMaxRetryCount(Integer maxRetryCount) {
}
public SuccessFactorsPluginConfig build() {
- return new SuccessFactorsPluginConfig(referenceName, baseURL, entityName, associateEntityName, username,
- password, proxyUrl, proxyUsername, proxyPassword,
- filterOption, selectOption, expandOption, additionalQueryParameters,
- paginationType, initialRetryDuration, maxRetryDuration,
- retryMultiplier, maxRetryCount);
+ return new SuccessFactorsPluginConfig(referenceName, baseURL, entityName, associateEntityName, username, password,
+ proxyUrl, proxyUsername, proxyPassword,
+ tokenURL, clientId, privateKey, expireInMinutes, userId, samlUsername, assertionToken,
+ companyId, authType, assertionTokenType, filterOption, selectOption,
+ expandOption, additionalQueryParameters, paginationType,
+ initialRetryDuration, maxRetryDuration, retryMultiplier, maxRetryCount);
}
}
}
diff --git a/src/main/java/io/cdap/plugin/successfactors/source/service/SuccessFactorsService.java b/src/main/java/io/cdap/plugin/successfactors/source/service/SuccessFactorsService.java
index 0e21ed6..9964fef 100644
--- a/src/main/java/io/cdap/plugin/successfactors/source/service/SuccessFactorsService.java
+++ b/src/main/java/io/cdap/plugin/successfactors/source/service/SuccessFactorsService.java
@@ -277,7 +277,7 @@ public ODataFeed readServiceEntityData(Edm edm, Long skip, Long top)
String nextLink = dataFeed.getFeedMetadata().getNextLink();
if (nextLink != null) {
nextUrl = nextLink;
- LOG.info("Next page url: {}", nextLink);
+ LOG.trace("Next page url: {}", nextLink);
}
}
return dataFeed;
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 9b85b0c..dc6d24a 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
@@ -16,12 +16,14 @@
package io.cdap.plugin.successfactors.source.transport;
+import com.google.common.base.Strings;
import dev.failsafe.Failsafe;
import dev.failsafe.FailsafeException;
import dev.failsafe.RetryPolicy;
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.common.util.SuccessFactorsAccessToken;
import io.cdap.plugin.successfactors.common.util.SuccessFactorsUtil;
import io.cdap.plugin.successfactors.connector.SuccessFactorsConnectorConfig;
import okhttp3.Authenticator;
@@ -53,12 +55,14 @@ public class SuccessFactorsTransporter {
static final String SERVICE_VERSION = "dataserviceversion";
private static final Logger LOG = LoggerFactory.getLogger(SuccessFactorsTransporter.class);
private static final long CONNECTION_TIMEOUT = 300;
-
- private SuccessFactorsConnectorConfig config;
+ private static final long WAIT_TIME = 5;
+ private static final long MAX_NUMBER_OF_RETRY_ATTEMPTS = 5;
+ private static String accessToken;
private Response response;
+ private final SuccessFactorsConnectorConfig config;
- public SuccessFactorsTransporter(SuccessFactorsConnectorConfig pluginConfig) {
- this.config = pluginConfig;
+ public SuccessFactorsTransporter(SuccessFactorsConnectorConfig config) {
+ this.config = config;
}
/**
@@ -173,11 +177,46 @@ private RetryPolicy