subscriptions = new HashMap<>();
+
+ private KeycloakConfiguration auth;
+
+ public SubscriptionConfiguration findSubscriptionPropsByTopicName(String topic) {
+ return subscriptions.get(topic);
+ }
+
+ public KeycloakConfiguration getAuthenticationProperties() {
+ return auth;
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/KeycloakConfiguration.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/KeycloakConfiguration.java
new file mode 100644
index 00000000..00d3ea45
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/KeycloakConfiguration.java
@@ -0,0 +1,15 @@
+package org.camunda.rpa.client.config;
+
+import lombok.Data;
+
+/**
+ * Keycloak Configuration
+ */
+@Data
+public class KeycloakConfiguration {
+
+ private String clientId;
+ private String clientSecret;
+ private String grantType;
+ private String tokenUri;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/RobotHandlerProperties.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/RobotHandlerProperties.java
new file mode 100644
index 00000000..980e7c97
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/RobotHandlerProperties.java
@@ -0,0 +1,28 @@
+package org.camunda.rpa.client.config;
+
+import org.camunda.rpa.client.data.constants.RobotType;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * robot handler properties
+ * author:Shibin Thomas
+ */
+@Data
+@ConfigurationProperties(prefix = "robot")
+public class RobotHandlerProperties {
+
+ /**
+ * Base directory of the robot.
+ * this can be left empty if working with normal deployment, default location will be then
+ * resources/robots folder
+ * In case of docker, this directory will be /opt/robots
+ */
+ private String baseDir;
+ /**
+ * Robot type will be based on the way how the robots run
+ * available options are rcc / cloud
+ */
+ private RobotType robotType;
+
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/SwaggerConfig.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/SwaggerConfig.java
new file mode 100644
index 00000000..cac760f1
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/SwaggerConfig.java
@@ -0,0 +1,35 @@
+package org.camunda.rpa.client.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig extends WebMvcConfigurerAdapter {
+
+ @Bean
+ public Docket api() {
+ return new Docket(DocumentationType.SWAGGER_2)
+ .select()
+ .apis(RequestHandlerSelectors.any())
+ .paths(PathSelectors.any())
+ .build();
+ }
+
+ @Override
+ public void addResourceHandlers(ResourceHandlerRegistry registry) {
+ registry.addResourceHandler("swagger-ui.html")
+ .addResourceLocations("classpath:/META-INF/resources/");
+
+ registry.addResourceHandler("/webjars/**")
+ .addResourceLocations("classpath:/META-INF/resources/webjars/");
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/auth/AuthResponse.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/auth/AuthResponse.java
new file mode 100644
index 00000000..3560b666
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/auth/AuthResponse.java
@@ -0,0 +1,10 @@
+package org.camunda.rpa.client.config.auth;
+
+/**
+ * @author Shibin Thomas
+ */
+public class AuthResponse {
+
+ private String access_token;
+ private String scope;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/auth/KeycloakContext.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/auth/KeycloakContext.java
new file mode 100644
index 00000000..e96e08ea
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/auth/KeycloakContext.java
@@ -0,0 +1,35 @@
+package org.camunda.rpa.client.config.auth;
+
+/**
+ * Keycloak context holding access token.
+ *
+ * @author Shibin Thomas
+ */
+public class KeycloakContext {
+
+ String accessToken;
+
+ private long expiresAt;
+
+ String refreshToken;
+
+ public KeycloakContext(String accessToken, long expiresInMillis, String refreshToken) {
+ this.accessToken = accessToken;
+ expiresAt = System.currentTimeMillis() + expiresInMillis - 2000;
+ this.refreshToken = refreshToken;
+ }
+
+ /**
+ * Checks if the token needs a refresh or not
+ * @return
+ */
+ public boolean needsRefresh() {
+ return System.currentTimeMillis() >= expiresAt;
+ }
+
+ public String getRefreshToken() {
+ return refreshToken;
+ }
+
+ public String getAccessToken() { return accessToken; }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/auth/KeycloakContextProvider.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/auth/KeycloakContextProvider.java
new file mode 100644
index 00000000..04d58fc9
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/auth/KeycloakContextProvider.java
@@ -0,0 +1,141 @@
+package org.camunda.rpa.client.config.auth;
+
+import org.camunda.rpa.client.config.KeycloakConfiguration;
+import org.camunda.rpa.client.exception.IdentityProviderException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.*;
+import org.springframework.util.MultiValueMap;
+import org.springframework.util.MultiValueMapAdapter;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.client.WebClient;
+import java.nio.charset.Charset;
+import java.util.*;
+
+import static org.camunda.rpa.client.util.ObjectUtil.*;
+import static org.apache.http.HttpHeaders.AUTHORIZATION;
+
+/**
+ * Keycloak context provider.
+ *
+ * Manages access tokens for then Keycloak REST API.
+ */
+public class KeycloakContextProvider {
+
+ private static final Logger LOG = LoggerFactory.getLogger(KeycloakContextProvider.class);
+
+ private KeycloakContext context;
+
+ WebClient webClient;
+
+ KeycloakConfiguration keycloakConfiguration;
+
+ /**
+ * Creates a new Keycloak context provider
+ * @param keycloakConfiguration the Keycloak configuration
+ * @param webClient REST template
+ */
+ public KeycloakContextProvider(KeycloakConfiguration keycloakConfiguration, WebClient webClient) {
+ this.keycloakConfiguration = keycloakConfiguration;
+ this.webClient = webClient;
+ }
+
+ /**
+ * Requests an access token for the configured Keycloak client.
+ * @return new Keycloak context holding the access token
+ */
+ private KeycloakContext openAuthorizationContext() {
+
+ String authToken = keycloakConfiguration.getClientId() + ":" + keycloakConfiguration.getClientSecret();
+ String encodedAuthToken = encodeToBase64(authToken);
+
+ try {
+ Map response = webClient.post().uri(keycloakConfiguration.getTokenUri())
+ .header(AUTHORIZATION, "Basic " + encodedAuthToken)
+ .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+ .body(BodyInserters.fromFormData("grant_type", keycloakConfiguration.getGrantType()))
+ .retrieve()
+ .bodyToMono(Map.class)
+ .block();
+
+ String accessToken = convertKeyToString(response, "access_token");
+ String refreshToken = convertKeyToString(response, "refresh_token");
+ long expiresInMillis = convertKeyToLong(response, "expires_in") * 1000;
+ return new KeycloakContext(accessToken, expiresInMillis, refreshToken);
+
+ } catch (Exception rce) {
+ LOG.error(rce.getMessage());
+ throw new IdentityProviderException("Unable to get access token from Keycloak server", rce);
+ }
+ }
+
+
+ /**
+ * Refreshs an access token for the configured Keycloak client.
+ * @return the refreshed Keycloak context holding the access token
+ */
+ private KeycloakContext refreshToken() {
+
+ String authToken = keycloakConfiguration.getClientId() + ":" + keycloakConfiguration.getClientSecret();
+ String encodedAuthToken = encodeToBase64(authToken);
+
+ Map> targetMap = new HashMap<>();
+ MultiValueMap formData = new MultiValueMapAdapter(targetMap);
+ formData.put("grant_type", Collections.singletonList("refresh_token"));
+ formData.put("refresh_token", Collections.singletonList(context.getRefreshToken()));
+
+ try {
+ Map response = webClient.post().uri(keycloakConfiguration.getTokenUri())
+ .header(AUTHORIZATION, "Basic " + encodedAuthToken)
+ .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+ .body(BodyInserters.fromFormData(formData))
+ .retrieve()
+ .bodyToMono(Map.class)
+ .block();
+
+ String accessToken = convertKeyToString(response, "access_token");
+ String refreshToken = convertKeyToString(response, "refresh_token");
+ long expiresInMillis = convertKeyToLong(response, "expires_in") * 1000;
+ return new KeycloakContext(accessToken, expiresInMillis, refreshToken);
+ } catch (Exception rce) {
+ LOG.error(rce.getMessage());
+ throw new IdentityProviderException("Unable to refresh access token from Keycloak server", rce);
+ }
+ }
+
+
+ /**
+ * Creates a valid request entity for the Keycloak management API.
+ * @return request entity with access token set
+ */
+ public String createApiRequestAccessToken() {
+ if (context == null) {
+ context = openAuthorizationContext();
+ } else if (context.needsRefresh()) {
+ if (context.getRefreshToken() == null) {
+ LOG.error("Error : missingRefreshToken");
+ context = openAuthorizationContext();
+ } else {
+ try {
+ context = refreshToken();
+ } catch (IdentityProviderException ipe) {
+ context = openAuthorizationContext();
+ }
+ }
+ }
+ return context.getAccessToken();
+ }
+
+
+ /**
+ * Invalidates the current authorization context forcing to request a new token.
+ */
+ public void invalidateToken() { context = null;
+ }
+
+ private String encodeToBase64(String decodedString) {
+ byte[] stringAsBytes = decodedString.getBytes(Charset.defaultCharset());
+ return Base64.getEncoder()
+ .encodeToString(stringAsBytes);
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/auth/KeycloakRequestProvider.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/auth/KeycloakRequestProvider.java
new file mode 100644
index 00000000..8d6b4c3c
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/auth/KeycloakRequestProvider.java
@@ -0,0 +1,32 @@
+package org.camunda.rpa.client.config.auth;
+
+import org.camunda.rpa.client.config.KeycloakConfiguration;
+import org.camunda.bpm.client.interceptor.ClientRequestContext;
+import org.camunda.bpm.client.interceptor.ClientRequestInterceptor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import static org.apache.http.HttpHeaders.AUTHORIZATION;
+
+/**
+ * Keycloak Request Provider
+ */
+@Component
+public class KeycloakRequestProvider implements ClientRequestInterceptor {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(KeycloakRequestProvider.class);
+
+ private static KeycloakContextProvider keycloakContextProvider = null;
+
+ public void init(KeycloakConfiguration configuration, WebClient webClient){
+ LOGGER.debug("Initialized KeycloakRequestProvider");
+ keycloakContextProvider = new KeycloakContextProvider(configuration, webClient);
+ }
+
+ public void intercept(ClientRequestContext requestContext) {
+ requestContext.addHeader(AUTHORIZATION, "Bearer " + keycloakContextProvider.createApiRequestAccessToken());
+ }
+
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/FormioProperties.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/FormioProperties.java
new file mode 100644
index 00000000..822245e9
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/FormioProperties.java
@@ -0,0 +1,11 @@
+package org.camunda.rpa.client.config.formsflow;
+
+import lombok.Data;
+
+@Data
+public class FormioProperties {
+
+ private boolean enabled;
+ private String url;
+ private FormioSecurity security;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/FormioSecurity.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/FormioSecurity.java
new file mode 100644
index 00000000..fc9fff78
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/FormioSecurity.java
@@ -0,0 +1,11 @@
+package org.camunda.rpa.client.config.formsflow;
+
+import lombok.Data;
+
+@Data
+public class FormioSecurity {
+
+ private String username;
+ private String password;
+ private String accessTokenUri;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/FormsflowProperties.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/FormsflowProperties.java
new file mode 100644
index 00000000..79d6f154
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/FormsflowProperties.java
@@ -0,0 +1,12 @@
+package org.camunda.rpa.client.config.formsflow;
+
+import lombok.Data;
+import org.camunda.rpa.client.config.formsflow.FormioProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@Data
+@ConfigurationProperties(prefix = "formsflow")
+public class FormsflowProperties {
+
+ private FormioProperties formio;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/auth/FormioContext.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/auth/FormioContext.java
new file mode 100644
index 00000000..2e837b28
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/auth/FormioContext.java
@@ -0,0 +1,24 @@
+package org.camunda.rpa.client.config.formsflow.auth;
+
+
+public class FormioContext {
+
+ String accessToken;
+
+ private long expiresAt;
+
+ public FormioContext(String accessToken, long expiresAt) {
+ this.accessToken = accessToken;
+ this.expiresAt = expiresAt + System.currentTimeMillis();
+ }
+
+ /**
+ * Checks if the token needs a refresh or not
+ * @return
+ */
+ public boolean needsRefresh() {
+ return System.currentTimeMillis() >= expiresAt;
+ }
+
+ public String getAccessToken() { return accessToken; }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/auth/FormioContextProvider.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/auth/FormioContextProvider.java
new file mode 100644
index 00000000..2f04c162
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/auth/FormioContextProvider.java
@@ -0,0 +1,118 @@
+package org.camunda.rpa.client.config.formsflow.auth;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.camunda.rpa.client.config.formsflow.FormioProperties;
+import org.camunda.rpa.client.exception.FormioIdentityException;
+import org.camunda.rpa.client.exception.FormioServiceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+public class FormioContextProvider {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FormioContextProvider.class);
+
+ private ObjectMapper bpmObjectMapper;
+
+ private FormioContext context;
+
+ private final WebClient webClient;
+
+ private final FormioProperties formioConfiguration;
+
+ /**
+ * Creates a new Formio context provider
+ * @param formioConfiguration the Formio configuration
+ * @param webClient REST template
+ */
+ public FormioContextProvider( WebClient webClient, FormioProperties formioConfiguration,ObjectMapper objectMapper) {
+ this.formioConfiguration = formioConfiguration;
+ this.webClient = webClient;
+ this.bpmObjectMapper = objectMapper;
+ }
+
+ /**
+ * Requests an access token for the configured Keycloak client.
+ * @return new Keycloak context holding the access token
+ */
+ private synchronized FormioContext openAuthorizationContext() {
+
+ if(context != null && !context.needsRefresh()) return context;
+
+ Map paramMap = new HashMap<>();
+ paramMap.put("email", formioConfiguration.getSecurity().getUsername());
+ paramMap.put("password", formioConfiguration.getSecurity().getPassword());
+ Map> dataMap = new HashMap<>();
+ dataMap.put("data", paramMap);
+
+ try{
+ String token = webClient.post().uri(formioConfiguration.getSecurity().getAccessTokenUri())
+ .bodyValue(dataMap)
+ .accept(MediaType.APPLICATION_JSON)
+ .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+ .exchangeToMono(data -> {
+ if(data.statusCode().is2xxSuccessful()){
+ return Mono.just(data.headers().header("x-jwt-token").get(0));
+ } else{
+ return Mono.error(new FormioServiceException("Exception occurred in getting x-jwt-token"+ ". Message Body: " +
+ data));
+ }
+ })
+ .block();
+
+ if(token == null) throw new FormioIdentityException("Access token is null");
+ long expiresAt = decodeExpiresAt(token);
+ return new FormioContext(token, expiresAt);
+ } catch (Exception rce) {
+ LOG.error(rce.getMessage());
+ throw new FormioIdentityException("Unable to get access token from formio server", rce);
+ }
+ }
+
+ /**
+ * Creates a valid request entity for the Keycloak management API.
+ * @return request entity with access token set
+ */
+ public String createFormioRequestAccessToken() {
+ if (context == null) {
+ LOG.info("context is null, creating new");
+ context = openAuthorizationContext();
+ } else if (context.needsRefresh()) {
+ try {
+ LOG.info("Token Need refresh");
+ context = openAuthorizationContext();
+ } catch (FormioIdentityException ipe) {
+ LOG.info("Token refresh failed");
+ context = openAuthorizationContext();
+ }
+ }
+ return context.getAccessToken();
+ }
+
+ private void clearAuthenticationContext(){
+ this.context = null;
+ }
+
+ private long decodeExpiresAt(String token) throws FormioIdentityException{
+ try {
+ String[] chunks = token.split("\\.");
+ Base64.Decoder decoder = Base64.getUrlDecoder();
+ String data = new String(decoder.decode(chunks[1]));
+ JsonNode dataNode = bpmObjectMapper.readTree(data);
+ long exp = dataNode.get("exp").asLong();
+ long iat = dataNode.get("iat").asLong();
+ return (exp - iat) * 1000;
+ } catch (JsonProcessingException | NullPointerException exc){
+ throw new FormioIdentityException("Unable to parse access token from formio server", exc);
+ }
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/auth/FormioRequestProvider.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/auth/FormioRequestProvider.java
new file mode 100644
index 00000000..75b852e7
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/formsflow/auth/FormioRequestProvider.java
@@ -0,0 +1,30 @@
+package org.camunda.rpa.client.config.formsflow.auth;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.camunda.bpm.client.interceptor.ClientRequestContext;
+import org.camunda.rpa.client.config.auth.KeycloakContextProvider;
+import org.camunda.rpa.client.config.auth.KeycloakRequestProvider;
+import org.camunda.rpa.client.config.formsflow.FormioProperties;
+import org.camunda.rpa.client.config.formsflow.FormsflowProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import static org.apache.http.HttpHeaders.AUTHORIZATION;
+
+@Component
+public class FormioRequestProvider {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(KeycloakRequestProvider.class);
+
+ private static FormioContextProvider formioContextProvider = null;
+
+ private FormioRequestProvider(WebClient webClient, FormsflowProperties formsflowProperties, ObjectMapper objectMapper){
+ formioContextProvider = new FormioContextProvider(webClient, formsflowProperties.getFormio(), objectMapper);
+ }
+
+ public void intercept(WebClient.RequestHeadersSpec requestHeadersSpec) {
+ requestHeadersSpec.header("x-jwt-token", formioContextProvider.createFormioRequestAccessToken());
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/impl/ClientAutoConfiguration.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/impl/ClientAutoConfiguration.java
new file mode 100644
index 00000000..1434c425
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/impl/ClientAutoConfiguration.java
@@ -0,0 +1,24 @@
+package org.camunda.rpa.client.config.impl;
+
+import org.camunda.bpm.client.spring.impl.client.ClientPostProcessor;
+import org.camunda.bpm.client.spring.impl.subscription.SubscriptionPostProcessor;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Shibin Thomas
+ */
+@Configuration
+public class ClientAutoConfiguration {
+
+ @Bean
+ public static SubscriptionPostProcessor subscriptionPostprocessor() {
+ return new SubscriptionPostProcessor(PropertiesAwareSpringTopicSubscription.class);
+ }
+
+ @Bean
+ public static ClientPostProcessor clientPostProcessor() {
+ return new ClientPostProcessor(PropertiesAwareClientFactory.class);
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/impl/PropertiesAwareClientFactory.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/impl/PropertiesAwareClientFactory.java
new file mode 100644
index 00000000..7370aaa8
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/impl/PropertiesAwareClientFactory.java
@@ -0,0 +1,80 @@
+package org.camunda.rpa.client.config.impl;
+
+import org.camunda.rpa.client.config.KeycloakConfiguration;
+import org.camunda.rpa.client.config.ClientProperties;
+import org.camunda.rpa.client.config.auth.KeycloakRequestProvider;
+import org.camunda.bpm.client.spring.impl.client.ClientConfiguration;
+import org.camunda.bpm.client.spring.impl.client.ClientFactory;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.reactive.function.client.WebClient;
+
+/**
+ * @author Shibin Thomas
+ */
+public class PropertiesAwareClientFactory extends ClientFactory {
+
+ @Autowired
+ private WebClient webClient;
+
+ @Autowired
+ protected ClientProperties clientProperties;
+
+ @Autowired
+ private KeycloakRequestProvider keycloakRequestProvider;
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ applyPropertiesFrom(clientProperties);
+ addKeycloakAuthInterceptor();
+ super.afterPropertiesSet();
+ }
+
+ protected void addKeycloakAuthInterceptor() {
+ KeycloakConfiguration keycloakConfiguration = clientProperties.getAuthenticationProperties();
+ if (keycloakConfiguration != null) {
+
+ keycloakRequestProvider.init(keycloakConfiguration, webClient);
+
+ getRequestInterceptors().add(keycloakRequestProvider);
+ }
+ }
+
+ public void applyPropertiesFrom(ClientProperties clientConfigurationProps) {
+ ClientConfiguration clientConfiguration = new ClientConfiguration();
+ if (clientConfigurationProps.getBaseUrl() != null) {
+ clientConfiguration.setBaseUrl(clientConfigurationProps.getBaseUrl());
+ }
+ if (clientConfigurationProps.getWorkerId() != null) {
+ clientConfiguration.setWorkerId(clientConfigurationProps.getWorkerId());
+ }
+ if (clientConfigurationProps.getMaxTasks() != null) {
+ clientConfiguration.setMaxTasks(clientConfigurationProps.getMaxTasks());
+ }
+ if (clientConfigurationProps.getUsePriority() != null && !clientConfigurationProps.getUsePriority()) {
+ clientConfiguration.setUsePriority(false);
+ }
+ if (clientConfigurationProps.getDefaultSerializationFormat() != null) {
+ clientConfiguration.setDefaultSerializationFormat(clientConfigurationProps.getDefaultSerializationFormat());
+ }
+ if (clientConfigurationProps.getDateFormat() != null) {
+ clientConfiguration.setDateFormat(clientConfigurationProps.getDateFormat());
+ }
+ if (clientConfigurationProps.getLockDuration() != null) {
+ clientConfiguration.setLockDuration(clientConfigurationProps.getLockDuration());
+ }
+ if (clientConfigurationProps.getAsyncResponseTimeout() != null) {
+ clientConfiguration.setAsyncResponseTimeout(clientConfigurationProps.getAsyncResponseTimeout());
+ }
+ if (clientConfigurationProps.getDisableAutoFetching() != null &&
+ clientConfigurationProps.getDisableAutoFetching()) {
+ clientConfiguration.setDisableAutoFetching(true);
+ }
+ if (clientConfigurationProps.getDisableBackoffStrategy() != null &&
+ clientConfigurationProps.getDisableBackoffStrategy()) {
+ clientConfiguration.setDisableBackoffStrategy(true);
+ }
+ setClientConfiguration(clientConfiguration);
+ }
+
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/impl/PropertiesAwareSpringTopicSubscription.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/impl/PropertiesAwareSpringTopicSubscription.java
new file mode 100644
index 00000000..03a2606c
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/config/impl/PropertiesAwareSpringTopicSubscription.java
@@ -0,0 +1,84 @@
+package org.camunda.rpa.client.config.impl;
+
+import org.camunda.rpa.client.config.ClientProperties;
+import org.camunda.bpm.client.spring.impl.subscription.SpringTopicSubscriptionImpl;
+import org.camunda.bpm.client.spring.impl.subscription.SubscriptionConfiguration;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.event.ApplicationStartedEvent;
+import org.springframework.context.ApplicationEvent;
+
+import java.util.function.Predicate;
+
+/**
+ * @author Shibin Thomas
+ */
+public class PropertiesAwareSpringTopicSubscription extends SpringTopicSubscriptionImpl {
+
+ @Autowired
+ protected ClientProperties clientProperties;
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ mergeSubscriptionWithProperties();
+ super.afterPropertiesSet();
+ }
+
+ @Override
+ protected Predicate isEventThatCanStartSubscription() {
+ return event -> event instanceof ApplicationStartedEvent;
+ }
+
+ protected void mergeSubscriptionWithProperties() {
+ SubscriptionConfiguration merge = getSubscriptionConfiguration();
+
+ String topicName = merge.getTopicName();
+ SubscriptionConfiguration subscriptionProperties =
+ clientProperties.findSubscriptionPropsByTopicName(topicName);
+
+ if (subscriptionProperties != null) {
+ if (subscriptionProperties.getAutoOpen() != null) {
+ merge.setAutoOpen(subscriptionProperties.getAutoOpen());
+ }
+ if (subscriptionProperties.getLockDuration() != null) {
+ merge.setLockDuration(subscriptionProperties.getLockDuration());
+ }
+ if (subscriptionProperties.getVariableNames() != null) {
+ merge.setVariableNames(subscriptionProperties.getVariableNames());
+ }
+ if (subscriptionProperties.getBusinessKey() != null) {
+ merge.setBusinessKey(subscriptionProperties.getBusinessKey());
+ }
+ if (subscriptionProperties.getProcessDefinitionId() != null) {
+ merge.setProcessDefinitionId(subscriptionProperties.getProcessDefinitionId());
+ }
+ if (subscriptionProperties.getProcessDefinitionIdIn() != null) {
+ merge.setProcessDefinitionIdIn(subscriptionProperties.getProcessDefinitionIdIn());
+ }
+ if (subscriptionProperties.getProcessDefinitionKey() != null) {
+ merge.setProcessDefinitionKey(subscriptionProperties.getProcessDefinitionKey());
+ }
+ if (subscriptionProperties.getProcessDefinitionKeyIn() != null) {
+ merge.setProcessDefinitionKeyIn(subscriptionProperties.getProcessDefinitionKeyIn());
+ }
+ if (subscriptionProperties.getProcessDefinitionVersionTag() != null) {
+ merge.setProcessDefinitionVersionTag(subscriptionProperties.getProcessDefinitionVersionTag());
+ }
+ if (subscriptionProperties.getProcessVariables() != null) {
+ merge.setProcessVariables(subscriptionProperties.getProcessVariables());
+ }
+ if (subscriptionProperties.getWithoutTenantId() != null) {
+ merge.setWithoutTenantId(subscriptionProperties.getWithoutTenantId());
+ }
+ if (subscriptionProperties.getTenantIdIn() != null) {
+ merge.setTenantIdIn(subscriptionProperties.getTenantIdIn());
+ }
+ if (subscriptionProperties.getIncludeExtensionProperties() != null) {
+ merge.setIncludeExtensionProperties(subscriptionProperties.getIncludeExtensionProperties());
+ }
+
+ setSubscriptionConfiguration(merge);
+ }
+ }
+
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/controller/CamundaRPAClientController.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/controller/CamundaRPAClientController.java
new file mode 100644
index 00000000..a15c2d0e
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/controller/CamundaRPAClientController.java
@@ -0,0 +1,82 @@
+package org.camunda.rpa.client.controller;
+
+import org.camunda.rpa.client.core.RobotDataManager;
+import org.camunda.rpa.client.data.entity.RobotHandlerConfig;
+import org.camunda.rpa.client.exception.RobotClientDataException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * This class make use of robot handler configuration table to do some
+ * operations
+ *
+ * @author Shibin Thomas
+ */
+@RestController
+@RequestMapping("/api/robot/")
+public class CamundaRPAClientController {
+
+ @Autowired
+ private RobotDataManager robotDataManager;
+
+ @GetMapping(value = "healthcheck")
+ public String healthCheck() {
+ return "Welcome to Camunda RPA Client";
+ }
+
+ /**
+ * Get list of robot configuration data
+ *
+ * @return
+ */
+ @GetMapping(value = "config/list", produces = "application/json")
+ public ResponseEntity> listConfigData() {
+
+ List response = null;
+ try {
+ response = robotDataManager.listRobotConfigData();
+ return ResponseEntity.ok(response);
+ } catch (RobotClientDataException ex) {
+ return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Update robot configuration data
+ *
+ * @return
+ */
+ @PutMapping(value = "config/update", produces = "application/json", consumes = "application/json")
+ public ResponseEntity updateConfigData(@RequestBody RobotHandlerConfig config) {
+ RobotHandlerConfig response = null;
+ try {
+ response = robotDataManager.updateRobotHandlerConfigData(config);
+ return ResponseEntity.ok(response);
+ } catch (RobotClientDataException ex) {
+ return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Save robot configuration data
+ *
+ * @return
+ */
+ @PostMapping(value = "config/save", produces = "application/json", consumes = "application/json")
+ public ResponseEntity saveConfigData(@RequestBody RobotHandlerConfig config) {
+ RobotHandlerConfig response = null;
+ try {
+ response = robotDataManager.saveRobotHandlerConfigData(config);
+ return ResponseEntity.ok(response);
+ } catch (RobotClientDataException ex) {
+ return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/IManager.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/IManager.java
new file mode 100644
index 00000000..52c6447d
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/IManager.java
@@ -0,0 +1,9 @@
+package org.camunda.rpa.client.core;
+
+/**
+ * author : Shibin Thomas
+ */
+public interface IManager {
+
+ void manage();
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/RobotDataManager.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/RobotDataManager.java
new file mode 100644
index 00000000..1b47ec48
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/RobotDataManager.java
@@ -0,0 +1,107 @@
+package org.camunda.rpa.client.core;
+
+import org.camunda.rpa.client.core.data.RobotHandlerAuditService;
+import org.camunda.rpa.client.data.entity.RobotHandlerAudit;
+import org.camunda.rpa.client.data.entity.RobotHandlerConfig;
+import org.camunda.rpa.client.data.repository.RepoFinder;
+import org.camunda.bpm.client.task.ExternalTask;
+import org.camunda.rpa.client.exception.RobotClientDataException;
+import org.camunda.rpa.client.exception.RobotClientRuntimeException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * RobotDataManager is the gateway / manager for com.camunda.rpa.client.core.data.*
+ * This class includes methods for managing the audit, handler configuration etc..
+ * author : Shibin Thomas
+ */
+@Component
+public class RobotDataManager {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RobotDataManager.class);
+
+ @Autowired
+ private RepoFinder repoFinder;
+ @Autowired
+ private RobotHandlerAuditService auditService;
+
+ /**
+ * initAudit can make a new entry on Audit table
+ * This method use Handler configuration to map robot with the audit.
+ *
+ * @param externalTask
+ * @param handlerId
+ * @return
+ */
+ public RobotHandlerAudit initAudit(ExternalTask externalTask, Integer handlerId) {
+ LOG.debug("Initializing audit for handler = " + handlerId);
+ Optional optional = repoFinder.getConfigRepository().findById(handlerId);
+ RobotHandlerConfig config = optional.orElse(null);
+ return auditService.createAudit(externalTask, config);
+ }
+
+ /**
+ * finalizeAudit will complete the audit process by invoking com.camunda.rpa.client.core.data.RobotHandlerAuditService
+ * updateAuditSuccess / updateAuditFailure by a flag.
+ * status flag will be true if the robot was ran successfully
+ * status flag will be true in case of a failure. On failure error message will be saved in audit.
+ *
+ * @param audit
+ * @param status
+ * @param errorDetails
+ */
+ public void finalizeAudit(RobotHandlerAudit audit, boolean status, String errorDetails) {
+
+ if (status) {
+ auditService.updateAuditSuccess(audit);
+ } else {
+ auditService.updateAuditFailure(audit, errorDetails);
+ }
+ }
+
+ /**
+ * Get All the robot configuration data
+ * @return
+ */
+ public List listRobotConfigData() {
+ return (ArrayList) repoFinder.getConfigRepository().findAll();
+ }
+
+ /**
+ * Update the robot configuration data
+ * @param robotHandlerConfig
+ * @return
+ */
+ public RobotHandlerConfig updateRobotHandlerConfigData(RobotHandlerConfig robotHandlerConfig) throws RobotClientDataException {
+
+ Integer handlerId = robotHandlerConfig != null ? robotHandlerConfig.getHandlerId() : null;
+ if (handlerId != null) {
+ if (repoFinder.getConfigRepository().findById(robotHandlerConfig.getHandlerId()).isPresent()) {
+ return repoFinder.getConfigRepository().save(robotHandlerConfig);
+ } else {
+ throw new RobotClientDataException("No suitable data found for handler id = " + handlerId);
+ }
+ } else {
+ throw new RobotClientDataException("No handler id present in the data");
+ }
+ }
+
+ /**
+ * Add a new robot configuration entry
+ * @param robotHandlerConfig
+ * @return
+ */
+ public RobotHandlerConfig saveRobotHandlerConfigData(RobotHandlerConfig robotHandlerConfig) throws RobotClientDataException {
+
+ if (robotHandlerConfig == null) {
+ throw new RobotClientDataException("No config data provided");
+ }
+ return repoFinder.getConfigRepository().save(robotHandlerConfig);
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/RobotIOManager.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/RobotIOManager.java
new file mode 100644
index 00000000..bc483094
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/RobotIOManager.java
@@ -0,0 +1,114 @@
+package org.camunda.rpa.client.core;
+
+import org.camunda.rpa.client.config.ClientProperties;
+import org.camunda.rpa.client.config.RobotHandlerProperties;
+import org.camunda.rpa.client.data.RobotInput;
+import org.camunda.rpa.client.data.TaskDataInput;
+import org.camunda.rpa.client.core.io.RobotInputService;
+import org.camunda.rpa.client.core.io.RobotResponseService;
+import org.camunda.rpa.client.data.constants.RobotResponseType;
+import org.camunda.rpa.client.data.constants.RobotType;
+import org.camunda.rpa.client.data.entity.RobotHandlerAudit;
+import org.camunda.rpa.client.data.entity.RobotHandlerConfig;
+import org.camunda.bpm.client.spring.impl.subscription.SubscriptionConfiguration;
+import org.camunda.bpm.client.task.ExternalTask;
+import org.camunda.bpm.engine.variable.VariableMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Manager class for Input / Response service classes. author : Shibin Thomas
+ */
+@Component
+public class RobotIOManager {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RobotIOManager.class);
+
+ @Autowired
+ private RobotInputService robotInputService;
+
+ @Autowired
+ private RobotResponseService robotResponseService;
+
+ @Autowired
+ private ClientProperties clientProperties;
+
+ @Autowired
+ private RobotHandlerProperties handlerProperties;
+
+ public List buildInput(ExternalTask externalTask, RobotHandlerConfig config,
+ Map additionalVariables) {
+
+ SubscriptionConfiguration subscriptionConfiguration = clientProperties.getSubscriptions()
+ .get(config.getTopicName());
+ TaskDataInput input = new TaskDataInput(externalTask, subscriptionConfiguration.getVariableNames(),
+ additionalVariables);
+
+ return robotInputService.buildInputVariables(input);
+ }
+
+ /**
+ * This function will generate a random number and assign it as the file name
+ * The output directory name will derive from the random number
+ *
+ * @param robotName
+ * @return workingDirName
+ */
+ public synchronized String getOutputDirName(String robotName) {
+ String workingDirName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
+ boolean status = robotResponseService.createOutputDirectory(robotName, workingDirName);
+
+ return status ? workingDirName : "-1";
+ }
+
+ /**
+ * This function will generate response for camunda Response type is
+ * configurable and based on the configuration the response will be generated.
+ *
+ * @param audit
+ * @return
+ */
+ public VariableMap getOutputData(RobotHandlerAudit audit, String formUrl) {
+
+ RobotHandlerConfig config = audit.getHandlerConfig();
+ VariableMap variableMap = null;
+ RobotType robotType = handlerProperties.getRobotType();
+
+ if (RobotResponseType.FILE.equals(config.getResponseType()) && RobotType.ROBOCORP_RCC.equals(robotType)) {
+ variableMap = robotResponseService.readRCCRobotFileResponse(config.getRobotName(),
+ config.getWorkingDirName(), formUrl);
+ }
+ if (RobotResponseType.FILE.equals(config.getResponseType()) && RobotType.ROBOCORP_CLOUD.equals(robotType)) {
+ try {
+ variableMap = robotResponseService.readRobocorpCloudResponseFile(audit, formUrl);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return variableMap;
+ }
+
+ /**
+ * This function will clear the robot current output directory and it's data
+ *
+ * @param audit
+ * @return
+ */
+ public boolean clearAndRemoveOutputDirectory(RobotHandlerAudit audit) {
+
+ RobotHandlerConfig config = audit.getHandlerConfig();
+ if (RobotResponseType.FILE.equals(config.getResponseType())) {
+ return robotResponseService.clearAndRemoveOutputDirectory(config.getRobotName(),
+ config.getWorkingDirName());
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/RobotManager.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/RobotManager.java
new file mode 100644
index 00000000..127da935
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/RobotManager.java
@@ -0,0 +1,62 @@
+package org.camunda.rpa.client.core;
+
+import org.camunda.rpa.client.config.RobotHandlerProperties;
+import org.camunda.rpa.client.core.robot.IRobotService;
+import org.camunda.rpa.client.data.RobotInput;
+import org.camunda.rpa.client.core.robot.RCCService;
+import org.camunda.rpa.client.core.robot.RobocorpCloudService;
+import org.camunda.rpa.client.data.constants.RobotType;
+import org.camunda.rpa.client.data.entity.RobotHandlerAudit;
+import org.camunda.rpa.client.data.entity.RobotHandlerConfig;
+import org.camunda.rpa.client.exception.RobotClientRuntimeException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * RobotManager manages robot services from com.camunda.rpa.client.core.robot.*
+ * This class will identify the service to run robots and manages them.
+ * author : Shibin Thomas
+ */
+@Component
+public class RobotManager {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RobotManager.class);
+
+ @Autowired
+ private RobotHandlerProperties handlerProperties;
+ @Autowired
+ private RCCService rccService;
+ @Autowired
+ private RobocorpCloudService robocorpCloudService;
+
+ /**
+ * runRobot run the robot service by using a user defined property to identify.
+ * @param robotInputs
+ * @param audit
+ */
+ public boolean runRobot(List robotInputs, RobotHandlerAudit audit){
+ IRobotService robotService = getRobotService();
+ return robotService.runRobot(robotInputs, audit);
+ }
+
+ // get robot services by using user defined {robotType} property - refer RobotType enum class
+ private IRobotService getRobotService(){
+
+ IRobotService robotService;
+ RobotType robotType = handlerProperties.getRobotType();
+
+ if (robotType == RobotType.ROBOCORP_CLOUD) {
+ robotService = robocorpCloudService;
+ } else if(RobotType.ROBOCORP_RCC.equals(robotType)){
+ robotService = rccService;
+ } else {
+ throw new RobotClientRuntimeException("No suitable service identified for robot type ="+robotType, HttpStatus.INTERNAL_SERVER_ERROR.value());
+ }
+ return robotService;
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/RobotPipelineManager.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/RobotPipelineManager.java
new file mode 100644
index 00000000..1d4ccdd6
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/RobotPipelineManager.java
@@ -0,0 +1,53 @@
+package org.camunda.rpa.client.core;
+
+import org.camunda.rpa.client.core.pipe.CommandRunnerService;
+import org.camunda.rpa.client.core.pipe.HoloTreeDictionary;
+import org.camunda.rpa.client.core.pipe.RobotDirectoryScanner;
+import org.camunda.rpa.client.exception.RobotClientRuntimeException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+/**
+ * rcc run --interactive --task scripting -- --variable answer:https://www.google.com tasks.robot
+ * author : Shibin Thomas
+ */
+@Component
+public class RobotPipelineManager {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RobotPipelineManager.class);
+
+ @Autowired
+ private CommandRunnerService commandRunnerService;
+ @Autowired
+ private RobotDirectoryScanner robotDirectoryScanner;
+
+
+ public String[] buildRobocorpRCCScript(String input, String robotName, String workingDirName, String taskFileName){
+
+ String[] scripts = new String[2];
+
+ File baseDir = robotDirectoryScanner.getRobotLocationMap().get(robotName);
+ if(baseDir == null){
+ throw new RobotClientRuntimeException("No appropriate robot directories identified", 500);
+ }
+ scripts[0] = "cd "+baseDir.getAbsolutePath();
+
+ scripts[1] = "rcc run "+
+ " --space "+HoloTreeDictionary.getHoloTreeInfo().get(robotName)+
+ " --interactive -- "+
+ (workingDirName != null ? " --outputdir output/"+workingDirName+" " : " ")+
+ (input == null?"":input)+
+ (taskFileName != null? " "+taskFileName:" tasks.robot");
+
+ return scripts;
+ }
+
+ public void runRobocorpRccScript(String[] scripts){
+ LOG.debug("Running robot scripts using rcc");
+ commandRunnerService.run(scripts);
+ }
+
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/data/IDataService.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/data/IDataService.java
new file mode 100644
index 00000000..2ffece8e
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/data/IDataService.java
@@ -0,0 +1,4 @@
+package org.camunda.rpa.client.core.data;
+
+public interface IDataService {
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/data/RobotHandlerAuditService.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/data/RobotHandlerAuditService.java
new file mode 100644
index 00000000..52b547bf
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/data/RobotHandlerAuditService.java
@@ -0,0 +1,73 @@
+package org.camunda.rpa.client.core.data;
+
+import org.camunda.rpa.client.core.RobotIOManager;
+import org.camunda.rpa.client.data.constants.RobotResponseType;
+import org.camunda.rpa.client.data.repository.RepoFinder;
+import org.camunda.rpa.client.exception.RobotClientRuntimeException;
+import org.camunda.bpm.client.task.ExternalTask;
+import org.camunda.rpa.client.data.constants.RobotStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import org.camunda.rpa.client.data.entity.RobotHandlerAudit;
+import org.camunda.rpa.client.data.entity.RobotHandlerConfig;
+
+/**
+ * Service class aimed to create / update an audit entry.
+ *
+ * @author Sneha Suresh
+ *
+ */
+@Service
+public class RobotHandlerAuditService implements IDataService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RobotHandlerAuditService.class);
+
+ @Autowired
+ private RobotIOManager robotIOManager;
+
+ @Autowired
+ private RepoFinder repoFinder;
+
+ /**
+ * This method is intended to make a new entry on Audit table
+ * @param externalTask
+ * @param config
+ * @return
+ */
+ public RobotHandlerAudit createAudit(ExternalTask externalTask, RobotHandlerConfig config) {
+ if (externalTask == null)
+ throw new RobotClientRuntimeException("ExternalTask data cannot be null", 500);
+ RobotHandlerAudit robotHandlerAudit = new RobotHandlerAudit();
+ robotHandlerAudit.setHandlerConfig(config);
+ robotHandlerAudit.setTaskId(externalTask.getId());
+ robotHandlerAudit.setStatus(RobotStatus.IN_PROGRESS);
+ if (RobotResponseType.FILE.equals(config.getResponseType()) || RobotResponseType.FILE_MULTI.equals(config.getResponseType())) {
+ config.setWorkingDirName(robotIOManager.getOutputDirName(config.getRobotName()));
+ }
+ return repoFinder.getAuditRepository().save(robotHandlerAudit);
+ }
+
+ /**
+ * The method updateAudit can make an update on Audit table
+ * @param robotHandlerAudit
+ */
+ public void updateAuditSuccess(RobotHandlerAudit robotHandlerAudit) {
+ robotHandlerAudit.setStatus(RobotStatus.SUCCESS);
+ repoFinder.getAuditRepository().save(robotHandlerAudit);
+ }
+
+ /**
+ * The method updateAuditFailure can make an update on Audit table
+ * @param robotHandlerAudit
+ * @param errorDetails
+ */
+ public void updateAuditFailure(RobotHandlerAudit robotHandlerAudit, String errorDetails) {
+ robotHandlerAudit.setDetails(errorDetails);
+ robotHandlerAudit.setStatus(RobotStatus.FAILED);
+ repoFinder.getAuditRepository().save(robotHandlerAudit);
+ }
+
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/RobotInputService.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/RobotInputService.java
new file mode 100644
index 00000000..e9e6373f
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/RobotInputService.java
@@ -0,0 +1,65 @@
+package org.camunda.rpa.client.core.io;
+
+import org.camunda.rpa.client.config.RobotHandlerProperties;
+import org.camunda.rpa.client.data.RobotInput;
+import org.camunda.rpa.client.data.TaskDataInput;
+import org.camunda.rpa.client.data.constants.RobotType;
+import org.camunda.bpm.client.task.ExternalTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class is responsible for building input data for the robots
+ * This receives data from handlers, which then extracted and send back as appropriate
+ * robot input data.
+ * author: Shibin Thomas
+ */
+@Service
+public class RobotInputService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RobotInputService.class);
+ @Autowired
+ private RobotHandlerProperties handlerProperties;
+
+ /**
+ * This function will build input variables for the robot.
+ * Handlers are invoking this service via the manager class
+ * @param input
+ * @return
+ */
+ public List buildInputVariables(TaskDataInput input){
+
+ ExternalTask externalTask = input.getExternalTask();
+ List variableNames = input.getVariableNames();
+
+ List robotInputs = new ArrayList<>();
+
+ for (String field : variableNames) {
+ Object value = externalTask.getVariable(field);
+ addRobotInputData(robotInputs, field, value);
+ }
+ if(input.getAdditionalVariables() != null){
+ for(Map.Entry entry : input.getAdditionalVariables().entrySet()){
+ addRobotInputData(robotInputs,entry.getKey(), entry.getValue());
+ }
+ }
+ return robotInputs;
+ }
+
+ private void addRobotInputData(List robotInputs, String field, Object value){
+ RobotType robotType = handlerProperties.getRobotType();
+ if(value instanceof String && robotType != RobotType.ROBOCORP_CLOUD){
+ String strValue = (String) value;
+ strValue = "\""+strValue+"\"";
+ robotInputs.add(new RobotInput(field, strValue));
+ } else {
+ robotInputs.add(new RobotInput(field, value));
+ }
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/RobotResponseService.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/RobotResponseService.java
new file mode 100644
index 00000000..0760b924
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/RobotResponseService.java
@@ -0,0 +1,210 @@
+package org.camunda.rpa.client.core.io;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.camunda.bpm.engine.variable.value.FileValue;
+import org.camunda.rpa.client.config.formsflow.FormsflowProperties;
+import org.camunda.rpa.client.config.formsflow.auth.FormioRequestProvider;
+import org.camunda.rpa.client.core.io.ro.Base64FileResponse;
+import org.camunda.rpa.client.core.io.ro.FormElement;
+import org.camunda.rpa.client.core.pipe.RobotDirectoryScanner;
+import java.io.InputStream;
+import org.camunda.rpa.client.data.constants.ExternalClientConstants;
+import org.camunda.rpa.client.data.entity.RobotHandlerAudit;
+import org.camunda.bpm.engine.variable.VariableMap;
+import org.camunda.bpm.engine.variable.Variables;
+import org.camunda.rpa.client.exception.RobotClientRuntimeException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.configurationprocessor.json.JSONException;
+import org.springframework.boot.configurationprocessor.json.JSONObject;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+/**
+ * This class is responsible for delivering output data from robots The response
+ * can be a file / multi files / string / array. author: Shibin Thomas
+ */
+@Service
+public class RobotResponseService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RobotResponseService.class);
+
+ @Autowired
+ private RobotDirectoryScanner robotDirectoryScanner;
+ @Autowired
+ private FormioRequestProvider formioRequestProvider;
+
+ private final WebClient webClient;
+
+ private final FormsflowProperties formsflowProperties;
+
+ @Value("${robot.cloud.api-key}")
+ private String apiKey;
+
+ private RobotResponseService(WebClient webClient, FormsflowProperties formsflowProperties) {
+ this.webClient = webClient;
+ this.formsflowProperties = formsflowProperties;
+ }
+
+ /**
+ * Read a single file from output directory and return it as response.
+ *
+ * @param robotName
+ * @param outputDirName
+ * @return
+ */
+ public VariableMap readRCCRobotFileResponse(String robotName, String outputDirName, String formUrl) {
+
+ LOG.debug("Reading response from output directory for robot = " + robotName);
+
+ VariableMap variables = Variables.createVariables();
+
+ File outputDir = robotDirectoryScanner.getRobotFinalDirectory(robotName, outputDirName);
+ File[] outputFileArray = outputDir.listFiles();
+
+ if ((outputFileArray != null ? outputFileArray.length : 0) > 0) {
+ File output = outputFileArray[0];
+
+ try {
+ // Need to check response type is base64 or not
+ if (true) {
+ String mimeType = URLConnection.guessContentTypeFromName(output.getName());
+ LOG.debug("mimetype = " + mimeType);
+ byte[] encoded = Base64.encodeBase64(FileUtils.readFileToByteArray(output));
+ String encodedData = new String(encoded, StandardCharsets.US_ASCII);
+
+ if (formsflowProperties.getFormio().isEnabled()) {
+ patchResponse(formUrl, output.getName(), encodedData, output.length());
+ }
+ }
+ FileValue typedFileValue = Variables.fileValue(output.getName()).file(output)
+ .mimeType("application/pdf").encoding("UTF-8").create();
+ variables.put(ExternalClientConstants.FILE_RESPONSE_VAR, typedFileValue);
+ } catch (Exception ex) {
+ LOG.error("Error : Reading response file");
+ ex.printStackTrace();
+ throw new RobotClientRuntimeException("Response file is not found",
+ HttpStatus.INTERNAL_SERVER_ERROR.value());
+ }
+ }
+ return variables;
+ }
+
+ private ResponseEntity patchResponse(String formUrl, String fileName, String encodedData, long length)
+ throws JsonProcessingException {
+ Base64FileResponse[] base64FileResponses = new Base64FileResponse[] {
+ new Base64FileResponse(fileName, encodedData, length) };
+
+ List elements = new ArrayList<>();
+
+ elements.add(new FormElement(ExternalClientConstants.FILE_RESPONSE_VAR, base64FileResponses));
+
+ WebClient.RequestHeadersSpec requestHeadersSpec = webClient.patch()
+ .uri(Objects.requireNonNull(getDecoratedServerUrl(formUrl)))
+ .bodyValue(new ObjectMapper().writeValueAsString(elements));
+
+ formioRequestProvider.intercept(requestHeadersSpec);
+
+ Mono> entityMono = requestHeadersSpec.accept(MediaType.APPLICATION_JSON)
+ .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).retrieve()
+ .onStatus(HttpStatus::is4xxClientError,
+ response -> Mono.error(new RobotClientRuntimeException(response.toString(), 404)))
+ .toEntity(String.class);
+
+ return entityMono.block();
+ }
+
+ /**
+ * This function will create temporary output file generated by the robot
+ *
+ * @param robotName
+ * @param outputDirName
+ * @return
+ */
+ public boolean createOutputDirectory(String robotName, String outputDirName) {
+ File outputDir = robotDirectoryScanner.getRobotLocationMap().get(robotName);
+ return robotDirectoryScanner.createOutputDirectory(outputDir, outputDirName);
+ }
+
+ /**
+ * This function will clear temporary output file generated by the robot
+ *
+ * @param robotName
+ * @param outputDirName
+ * @return
+ */
+ public boolean clearAndRemoveOutputDirectory(String robotName, String outputDirName) {
+ File outputDir = robotDirectoryScanner.getRobotWorkingDirectory(robotName, outputDirName);
+ return robotDirectoryScanner.deleteDirectory(outputDir);
+ }
+
+ private String getDecoratedServerUrl(String url) {
+ if (StringUtils.contains(url, "/form/")) {
+ return formsflowProperties.getFormio().getUrl() + "/form/" + StringUtils.substringAfter(url, "/form/");
+ }
+ return null;
+ }
+
+ /**
+ * Read a file from outputUrl and return it as response.
+ *
+ * @param audit
+ * @param formUrl
+ * @return
+ */
+ public VariableMap readRobocorpCloudResponseFile(RobotHandlerAudit audit, String formUrl)
+ throws JSONException, MalformedURLException, IOException {
+ VariableMap variables = Variables.createVariables();
+ String fileName = "data.pdf";
+ try {
+ ResponseEntity finalResponse = webClient.method(HttpMethod.GET).uri(audit.getResponseUri())
+ .header("Authorization", getApiKey()).accept(MediaType.APPLICATION_JSON)
+ .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).body(null).retrieve()
+ .toEntity(String.class).block();
+
+ LOG.error(finalResponse.getBody());
+ JSONObject jsonObject = new JSONObject(finalResponse.getBody());
+ InputStream in = new URL(jsonObject.getString("url")).openStream();
+ byte[] encoded = Base64.encodeBase64(in.readAllBytes());
+ String encodedData = new String(encoded, StandardCharsets.US_ASCII);
+
+ patchResponse(formUrl, fileName, encodedData, encodedData.length());
+
+ FileValue typedFileValue = Variables.fileValue(fileName).file(encoded).mimeType("application/pdf")
+ .encoding("UTF-8").create();
+ variables.put(ExternalClientConstants.FILE_RESPONSE_VAR, typedFileValue);
+ } catch (Exception ex) {
+ LOG.error("Error : Reading response file");
+ ex.printStackTrace();
+ throw new RobotClientRuntimeException("Response file is not found",
+ HttpStatus.INTERNAL_SERVER_ERROR.value());
+ }
+
+ return variables;
+ }
+
+ private String getApiKey() {
+ return this.apiKey;
+ }
+
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/parser/ParserService.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/parser/ParserService.java
new file mode 100644
index 00000000..4d4e50e7
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/parser/ParserService.java
@@ -0,0 +1,12 @@
+package org.camunda.rpa.client.core.io.parser;
+
+import org.camunda.rpa.client.core.io.ro.RobotOutput;
+import org.jdom2.JDOMException;
+
+import java.io.File;
+import java.io.IOException;
+
+public interface ParserService {
+
+ RobotOutput parse(File file) throws IOException, JDOMException;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/parser/RobotOutputXMLParser.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/parser/RobotOutputXMLParser.java
new file mode 100644
index 00000000..7fbe154b
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/parser/RobotOutputXMLParser.java
@@ -0,0 +1,97 @@
+package org.camunda.rpa.client.core.io.parser;
+
+import org.camunda.rpa.client.core.io.ro.RobotOutput;
+import org.camunda.rpa.client.core.io.ro.RobotOutputMessage;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
+import org.jdom2.input.SAXBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * XML parser to parse robot output.xml file
+ * output.xml will be produced once the robot completes it's execution
+ * author : Shibin Thomas
+ */
+@Component
+public class RobotOutputXMLParser implements ParserService{
+
+ private static final Logger LOG = LoggerFactory.getLogger(RobotOutputXMLParser.class);
+
+ /**
+ * To parse output.xml file produced by the robot
+ * @param file
+ * @return
+ */
+ @Override
+ public RobotOutput parse(File file) {
+
+ LOG.debug("START : Parsing output file to find the status");
+
+ RobotOutput robotOutput = null;
+ try {
+ SAXBuilder saxBuilder = new SAXBuilder();
+ Document document = saxBuilder.build(file);
+
+ LOG.debug("Root element :" + document.getRootElement().getName());
+
+ Element robotElement = document.getRootElement();
+
+ Element statistics = robotElement.getChild("statistics");
+ Element total = statistics.getChild("total");
+ Element stat = total.getChild("stat");
+ String pass = stat.getAttributeValue("pass");
+ String fail = stat.getAttributeValue("fail");
+ String skip = stat.getAttributeValue("skip");
+
+ Element errors = robotElement.getChild("errors");
+ List messages = errors.getChildren("msg");
+ List messageList = new ArrayList<>();
+ if(messages != null){
+ for(Element msg : messages){
+ String level = msg.getAttributeValue("level");
+ String message = msg.getText();
+ RobotOutputMessage robotOutputMessage = new RobotOutputMessage(level, message);
+ messageList.add(robotOutputMessage);
+ }
+ }
+ robotOutput = new RobotOutput(Integer.valueOf(pass), Integer.valueOf(fail), Integer.valueOf(skip), messageList);
+ if(robotOutput.getFail() > 0){
+ List suites = robotElement.getChildren("suite");
+ List errorMessages = findErrorDetails(suites);
+ if(errorMessages.size() > 0) {
+ for(String message : errorMessages){
+ RobotOutputMessage robotOutputMessage = new RobotOutputMessage("FAIL", message);
+ messageList.add(robotOutputMessage);
+ }
+ }
+ }
+ } catch (JDOMException | IOException ex){
+ ex.printStackTrace();
+ }
+ LOG.debug("END : Parsing output file to find the status");
+ return robotOutput;
+ }
+
+ private List findErrorDetails(List suites){
+
+ List errorMessages = new ArrayList<>();
+ for(Element suite : suites) {
+ List tests = suite.getChildren("test");
+ for (Element test : tests) {
+ Element status = test.getChild("status");
+ if (status.getAttributeValue("status").equals("FAIL")) {
+ errorMessages.add(status.getText());
+ }
+ }
+ }
+ return errorMessages;
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/Base64FileResponse.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/Base64FileResponse.java
new file mode 100644
index 00000000..cabba054
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/Base64FileResponse.java
@@ -0,0 +1,35 @@
+package org.camunda.rpa.client.core.io.ro;
+import java.io.Serializable;
+
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class Base64FileResponse implements IRO{
+
+ private static final long serialVersionUID = 1L;
+
+ public Base64FileResponse(String name, String url, Long size) {
+ this.name = name;
+ this.url = "data:application/pdf;base64,"+url;
+ this.size = size;
+ }
+
+ private String storage = "base64";
+ private String name;
+ private String url;
+
+ private Long size;
+
+ @Override
+ public String toString() {
+ return "Base64FileResponse{" +
+ "storage='" + storage + '\'' +
+ ", name='" + name + '\'' +
+ ", url='" + url + '\'' +
+ ", size=" + size +
+ '}';
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/FormElement.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/FormElement.java
new file mode 100644
index 00000000..d03d4a68
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/FormElement.java
@@ -0,0 +1,22 @@
+package org.camunda.rpa.client.core.io.ro;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author Sumathi Thirumani
+ * @author Shibin Thomas
+ */
+@Data
+@NoArgsConstructor
+public class FormElement {
+ private String op;
+ private String path;
+ private Object value;
+
+ public FormElement(String elementId, Object value) {
+ this.op = "replace";
+ this.path = "/data/" + elementId;
+ this.value = value;
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/IRO.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/IRO.java
new file mode 100644
index 00000000..b52085b3
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/IRO.java
@@ -0,0 +1,8 @@
+package org.camunda.rpa.client.core.io.ro;
+
+import java.io.Serializable;
+
+public interface IRO extends Serializable {
+
+ long serialVersionUID = 1L;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/RobotOutput.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/RobotOutput.java
new file mode 100644
index 00000000..3b9eecb4
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/RobotOutput.java
@@ -0,0 +1,29 @@
+package org.camunda.rpa.client.core.io.ro;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class RobotOutput implements IRO{
+
+ private Integer pass;
+ private Integer fail;
+ private Integer skip;
+
+ private List messages;
+
+ @Override
+ public String toString() {
+ return "RobotOutput{" +
+ "pass=" + pass +
+ ", fail=" + fail +
+ ", skip=" + skip +
+ ", messages=" + messages +
+ '}';
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/RobotOutputMessage.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/RobotOutputMessage.java
new file mode 100644
index 00000000..e5d0ae80
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/io/ro/RobotOutputMessage.java
@@ -0,0 +1,22 @@
+package org.camunda.rpa.client.core.io.ro;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class RobotOutputMessage {
+
+ private String level;
+ private String message;
+
+ @Override
+ public String toString() {
+ return "{" +
+ "level='" + level + '\'' +
+ ", message='" + message + '\'' +
+ '}';
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/pipe/CommandRunnerService.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/pipe/CommandRunnerService.java
new file mode 100644
index 00000000..9b2b0660
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/pipe/CommandRunnerService.java
@@ -0,0 +1,56 @@
+package org.camunda.rpa.client.core.pipe;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.io.PrintWriter;
+
+/**
+ * This class is responsible for managing service layer of command runner.
+ *
+ * @author Shibin Thomas
+ */
+@Service
+public class CommandRunnerService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CommandRunnerService.class);
+
+ public void run(String[] commands) {
+
+ LOG.debug("running commands using command runner service");
+
+ String os = System.getProperty("os.name");
+ String[] command = null;
+
+ if("Linux".equals(os)){
+ command = new String[]{ "/bin/bash", };
+ } else {
+ command = new String[]{ "cmd", };
+ }
+ Process p = null;
+ PrintWriter stdin = null;
+ try {
+ p = Runtime.getRuntime().exec(command);
+ new Thread(new SyncPipe(p.getErrorStream(), System.err)).start();
+ new Thread(new SyncPipe(p.getInputStream(), System.out)).start();
+
+ stdin = new PrintWriter(p.getOutputStream());
+
+ for(String cmd : commands){
+ stdin.println(cmd);
+ }
+
+ stdin.close();
+ p.waitFor();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if(stdin != null){
+ stdin.close();
+ }
+ if(p != null)
+ p.destroy();
+ }
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/pipe/HoloTreeDictionary.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/pipe/HoloTreeDictionary.java
new file mode 100644
index 00000000..a657365c
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/pipe/HoloTreeDictionary.java
@@ -0,0 +1,21 @@
+package org.camunda.rpa.client.core.pipe;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class HoloTreeDictionary {
+
+ private static Map holoTreeInfo = new HashMap<>();
+
+ public static Map getHoloTreeInfo() {
+ return holoTreeInfo;
+ }
+
+ public static void setHoloTreeInfo(List robotList) {
+ String space = "space-";
+ for(int i = 0; i < robotList.size(); i++) {
+ HoloTreeDictionary.holoTreeInfo.put( robotList.get(i), (space+i) );
+ }
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/pipe/RobotDirectoryScanner.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/pipe/RobotDirectoryScanner.java
new file mode 100644
index 00000000..4c08a446
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/pipe/RobotDirectoryScanner.java
@@ -0,0 +1,225 @@
+package org.camunda.rpa.client.core.pipe;
+
+import org.apache.commons.io.FileUtils;
+import org.camunda.rpa.client.config.RobotHandlerProperties;
+import org.camunda.rpa.client.data.constants.ExternalClientConstants;
+import org.camunda.rpa.client.exception.RobotClientRuntimeException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ResourceUtils;
+
+import javax.annotation.PostConstruct;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * This class will manage the creation / deletion of the robot directory and it's discovery
+ * author : Shibin Thomas
+ */
+@Component
+public class RobotDirectoryScanner {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RobotDirectoryScanner.class);
+
+ @Autowired
+ private RobotHandlerProperties handlerProperties;
+
+ private Map robotLocationMap;
+
+ /**
+ *
+ * @throws FileNotFoundException
+ */
+ @PostConstruct
+ public void scanRobotDirectory() throws FileNotFoundException {
+ robotLocationMap = new HashMap<>();
+ String baseDir = handlerProperties.getBaseDir();
+ File robotDir = null;
+ if(baseDir == null || baseDir.length() == 0) {
+ File file = ResourceUtils.getFile("classpath:robots");
+ if (file.isDirectory()) {
+ robotDir = new File(file.getAbsolutePath());
+ }
+ } else {
+ robotDir = new File(baseDir);
+ if(!robotDir.exists()){
+ throw new FileNotFoundException("No directory exists for the robot base directory");
+ }
+ }
+ if(robotDir != null) {
+ searchRobotConfigFileAndMark(robotDir.listFiles());
+ }
+ Collection coll = robotLocationMap.keySet();
+ List list;
+ if (coll instanceof List)
+ list = (List)coll;
+ else
+ list = new ArrayList<>(coll);
+ HoloTreeDictionary.setHoloTreeInfo(list);
+ }
+
+ /**
+ * Search the list of directories and identify if it has a robot configuration file
+ * @param dirs
+ */
+ private void searchRobotConfigFileAndMark(File[] dirs){
+
+ if(dirs == null) return;
+
+ for(File dir : dirs){
+ if(dir.isDirectory()) {
+ List allFiles = Arrays.asList(Objects.requireNonNull(dir.list()));
+ if (allFiles.size() > 0 && allFiles.contains(ExternalClientConstants.ROBOT_CONFIG_FILE)) {
+ String robotName = dir.getName();
+ robotLocationMap.put(robotName, dir);
+ } else {
+ searchRobotConfigFileAndMark(dir.listFiles());
+ }
+ }
+ }
+ }
+
+ /**
+ * Fetches the final directory to get the response data
+ * @param robotName
+ * @param workingDirName
+ * @return
+ */
+ public File getRobotFinalDirectory(String robotName, String workingDirName) throws RobotClientRuntimeException {
+
+ File robotDir = getRobotLocationMap().get(robotName);
+ if(robotDir == null){
+ throw new RobotClientRuntimeException("No appropriate robot directories identified", 500);
+ }
+ return getRobotFinalDirectory(robotDir, workingDirName);
+ }
+
+ /**
+ * Get the robot output directory which has the robot response
+ * @param robotName
+ * @param outputDirName
+ * @return
+ * @throws RobotClientRuntimeException
+ */
+ public File getRobotWorkingDirectory(String robotName, String outputDirName) throws RobotClientRuntimeException{
+
+ File robotDir = getRobotLocationMap().get(robotName);
+ if(robotDir == null){
+ throw new RobotClientRuntimeException("No appropriate robot directories identified", 500);
+ }
+ return getRobotWorkingDirectory(robotDir, outputDirName);
+ }
+
+
+ /**
+ * Get the robot output directory which has the robot response
+ * Eg structure : *** 178278234 *******
+ * ***** final ******************** runtime *******
+ * @param robotDir
+ * @param workingDirName
+ * @return
+ */
+ private File getRobotWorkingDirectory(File robotDir, String workingDirName){
+
+ File outputDir = null;
+
+ for(File file : Objects.requireNonNull(robotDir.listFiles())){
+ if(file.isDirectory() && file.getName().equals(ExternalClientConstants.OUTPUT_DIR)){
+ List outputFileList = Arrays.asList(Objects.requireNonNull(file.listFiles()));
+ List matchingFileList = outputFileList.stream().filter(f -> (f.isDirectory() && f.getName().equals(workingDirName))).collect(Collectors.toList());
+ if(matchingFileList.size() > 0) {
+ outputDir = matchingFileList.get(0);
+ }
+ break;
+ }
+ }
+ return outputDir;
+ }
+
+ /**
+ * Fetches the final directory to get the response data
+ * @param robotDir
+ * @param workingDirName
+ * @return
+ */
+ private File getRobotFinalDirectory(File robotDir, String workingDirName){
+
+ File finalDir = null;
+
+ for(File file : Objects.requireNonNull(robotDir.listFiles())){
+ if(file.isDirectory() && file.getName().equals(ExternalClientConstants.OUTPUT_DIR)){
+ List outputFileList = Arrays.asList(Objects.requireNonNull(file.listFiles()));
+ List matchingFileList = outputFileList.stream().filter(f -> (f.isDirectory() && f.getName().equals(workingDirName))).collect(Collectors.toList());
+ if(matchingFileList.size() > 0) {
+ for (File finalFile : Objects.requireNonNull(matchingFileList.get(0).listFiles())) {
+ if (finalFile.getName().equals(ExternalClientConstants.RESPONSE_DIR)) {
+ finalDir = finalFile;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ return finalDir;
+ }
+
+ /**
+ * Create the output directory for robots.
+ * @param outputDir
+ * @param outputDirName
+ * @return
+ */
+ public boolean createOutputDirectory(File outputDir, String outputDirName){
+
+ LOG.debug("Building output directories for the robot response");
+
+ boolean creationStatus = false;
+
+ try {
+
+ File rootDir = new File(outputDir.getAbsolutePath()
+ + ExternalClientConstants.BACK_SLASH
+ + ExternalClientConstants.OUTPUT_DIR
+ + ExternalClientConstants.BACK_SLASH
+ + outputDirName);
+ if (!rootDir.exists()) creationStatus = rootDir.mkdirs();
+
+ File runtimeFile = new File(rootDir.getAbsolutePath()
+ + ExternalClientConstants.BACK_SLASH
+ + ExternalClientConstants.RUNTIME_DIR);
+ File finalFile = new File(rootDir.getAbsolutePath()
+ + ExternalClientConstants.BACK_SLASH
+ + ExternalClientConstants.RESPONSE_DIR);
+ if (!runtimeFile.exists()) creationStatus = creationStatus && runtimeFile.mkdirs();
+ if (!finalFile.exists()) creationStatus = creationStatus && finalFile.mkdirs();
+ } catch (Exception ex){
+ ex.printStackTrace();
+ throw new RobotClientRuntimeException("Exception during output directory creation", HttpStatus.INTERNAL_SERVER_ERROR.value());
+ }
+
+ return creationStatus;
+ }
+
+ /**
+ * Deletes a directory
+ * @param outputDir
+ * @return
+ */
+ public boolean deleteDirectory(File outputDir){
+ if(outputDir == null) return false;
+ return FileUtils.deleteQuietly(outputDir);
+ }
+
+ /**
+ * @return the map of robots and it's location
+ */
+ public Map getRobotLocationMap() {
+ return robotLocationMap;
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/pipe/SyncPipe.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/pipe/SyncPipe.java
new file mode 100644
index 00000000..7e9fd3f2
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/pipe/SyncPipe.java
@@ -0,0 +1,28 @@
+package org.camunda.rpa.client.core.pipe;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+class SyncPipe implements Runnable
+{
+ public SyncPipe(InputStream istrm, OutputStream ostrm) {
+ istrm_ = istrm;
+ ostrm_ = ostrm;
+ }
+ public void run() {
+ try
+ {
+ final byte[] buffer = new byte[1024];
+ for (int length = 0; (length = istrm_.read(buffer)) != -1; )
+ {
+ ostrm_.write(buffer, 0, length);
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ private final OutputStream ostrm_;
+ private final InputStream istrm_;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/robot/IRobotService.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/robot/IRobotService.java
new file mode 100644
index 00000000..7248aea1
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/robot/IRobotService.java
@@ -0,0 +1,15 @@
+package org.camunda.rpa.client.core.robot;
+
+import org.camunda.rpa.client.data.RobotInput;
+import org.camunda.rpa.client.data.entity.RobotHandlerAudit;
+import org.camunda.rpa.client.data.entity.RobotHandlerConfig;
+
+import java.util.List;
+
+/**
+ * author : Shibin Thomas
+ */
+public interface IRobotService {
+
+ boolean runRobot(List robotInputs, RobotHandlerAudit audit);
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/robot/RCCService.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/robot/RCCService.java
new file mode 100644
index 00000000..3315e114
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/robot/RCCService.java
@@ -0,0 +1,158 @@
+package org.camunda.rpa.client.core.robot;
+
+import org.camunda.rpa.client.core.io.parser.RobotOutputXMLParser;
+import org.camunda.rpa.client.core.io.ro.RobotOutput;
+import org.camunda.rpa.client.core.pipe.RobotDirectoryScanner;
+import org.camunda.rpa.client.data.RobotInput;
+import org.camunda.rpa.client.core.RobotPipelineManager;
+import org.camunda.rpa.client.data.constants.ExternalClientConstants;
+import org.camunda.rpa.client.data.entity.RobotHandlerAudit;
+import org.camunda.rpa.client.data.entity.RobotHandlerConfig;
+import org.camunda.rpa.client.exception.RobotClientRuntimeException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * This class is implemented to run the robot by making a script with formatted input
+ *
+ * author : Shibin Thomas
+ */
+@Service
+public class RCCService implements IRobotService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RCCService.class);
+
+ private static final String format = " --variable ";
+
+ @Autowired
+ private RobotPipelineManager robotPipelineManager;
+
+ @Autowired
+ private RobotDirectoryScanner robotDirectoryScanner;
+
+ @Autowired
+ private RobotOutputXMLParser xmlParser;
+
+ @Override
+ public boolean runRobot(List robotInputs, RobotHandlerAudit audit) throws RobotClientRuntimeException{
+
+ RobotHandlerConfig config = audit.getHandlerConfig();
+ String[] scripts;
+ String workingDirName = config.getWorkingDirName();
+ if(robotInputs != null) {
+ String[] formattedInput = buildInput(robotInputs);
+ scripts = makeScriptWithInput(formattedInput, config.getRobotName(), workingDirName, config.getTaskFileName());
+ } else {
+ scripts = makeScriptWithoutInput(config.getRobotName(), workingDirName, config.getTaskFileName());
+ }
+ if(scripts == null || scripts.length == 0){
+ throw new RobotClientRuntimeException("No script to run robots ", 500);
+ }
+ runScript(scripts);
+
+ return checkRobotOutputStatus(config.getRobotName(), workingDirName);
+ }
+
+ /**
+ * Get List of robot Inputs and return it as response.
+ * @param robotInputs
+ * @return
+ */
+ private String[] buildInput(List robotInputs){
+
+ String[] formattedInput = new String[robotInputs.size()];
+
+ for(int i = 0; i < robotInputs.size(); i++){
+ formattedInput[i] = robotInputs.get(i).getField()+":"+robotInputs.get(i).getValue();
+ }
+ return formattedInput;
+ }
+
+ /**
+ *
+ * @param robotName
+ * @param workingDirName
+ * @param taskFileName
+ * @return
+ */
+ private String[] makeScriptWithoutInput(String robotName, String workingDirName, String taskFileName){
+
+ return buildScript(null, robotName, workingDirName, taskFileName);
+ }
+
+ /**
+ * This method is responsible to make script with available inputs and robot name
+ * @param inputs
+ * param robotName
+ * param workingDirName
+ * param taskFileName
+ * @return
+ */
+ private String[] makeScriptWithInput(String[] inputs, String robotName, String workingDirName, String taskFileName){
+
+ StringBuilder sb = new StringBuilder();
+ for(String input : inputs){
+ sb.append(format).append(input);
+ }
+ return buildScript(sb.toString(), robotName, workingDirName, taskFileName);
+ }
+
+ /**
+ * This method is responsible to build the script
+ * @param variables
+ * param robotName
+ * param taskFileName
+ * @return
+ */
+ private String[] buildScript(String variables, String robotName, String workingDirName, String taskFileName){
+ return robotPipelineManager.buildRobocorpRCCScript(variables, robotName, workingDirName, taskFileName);
+ }
+
+ /**
+ * This method is responsible to run the script
+ * @param scripts
+ */
+ private void runScript(String[] scripts){
+ robotPipelineManager.runRobocorpRccScript(scripts);
+ }
+
+ /**
+ * Robot output status will be validated based on the fail count
+ * fail count > 0 if any of the task failed to run
+ * fail count = 0 and pass > 0 and skip = 0 if all task completed successfully
+ * @param robotName
+ * @param workingDirName
+ * @return
+ * @throws RobotClientRuntimeException
+ */
+ private boolean checkRobotOutputStatus(String robotName, String workingDirName) throws RobotClientRuntimeException{
+ File outputDir = robotDirectoryScanner.getRobotWorkingDirectory(robotName, workingDirName);
+ File[] outputFiles = outputDir.listFiles();
+ LOG.debug("Working directory = "+workingDirName);
+ if(outputFiles != null) {
+ for (File file : outputFiles) {
+ if(file.getName().equals(ExternalClientConstants.ROBOT_OUTPUT_XML_FILE)){
+ RobotOutput robotOutput = xmlParser.parse(file);
+ if(robotOutput.getFail() > 0){
+ LOG.error("Robot Failed completing All the tasks - "+robotOutput.getMessages().toString());
+ throw new RobotClientRuntimeException("Robot Failed completing All the tasks - "+
+ robotOutput.getMessages().toString(),
+ HttpStatus.INTERNAL_SERVER_ERROR.value());
+ } else if(robotOutput.getSkip() > 0){
+ LOG.warn(robotOutput.getSkip()+" Tasks didn't complete");
+ return true;
+ } else {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/robot/RobocorpCloudService.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/robot/RobocorpCloudService.java
new file mode 100644
index 00000000..81e3b44d
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/core/robot/RobocorpCloudService.java
@@ -0,0 +1,138 @@
+package org.camunda.rpa.client.core.robot;
+
+import org.camunda.rpa.client.data.entity.RobotHandlerAudit;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.camunda.rpa.client.core.pipe.RobotDirectoryScanner;
+import org.camunda.rpa.client.data.RobotInput;
+import org.camunda.rpa.client.data.entity.RobotHandlerConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.configurationprocessor.json.JSONArray;
+import org.springframework.boot.configurationprocessor.json.JSONException;
+import org.springframework.boot.configurationprocessor.json.JSONObject;
+import org.springframework.stereotype.Service;
+import java.io.IOException;
+import java.util.List;
+import reactor.core.publisher.Mono;
+import org.springframework.http.*;
+
+/**
+ * This class will enable running robots in Robocorp cloud author : Shibin
+ * Thomas
+ */
+@Service
+public class RobocorpCloudService implements IRobotService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RobocorpCloudService.class);
+
+ @Autowired
+ private WebClient webClient;
+
+ @Autowired
+ private RobotDirectoryScanner robotDirectoryScanner;
+
+ @Value("${robot.cloud.api-key}")
+ private String apiKey;
+
+ @Value("${robot.cloud.api-url}")
+ private String processApi;
+
+ @Override
+ public boolean runRobot(List robotInputs, RobotHandlerAudit audit) {
+ try {
+ return invokeRobot(robotInputs, audit);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ private boolean invokeRobot(List robotInputs, RobotHandlerAudit audit) throws JSONException {
+ RobotHandlerConfig config = audit.getHandlerConfig();
+ String response = startRobotProcess(robotInputs, config);
+ JSONObject jsonObject = new JSONObject(response);
+ String robotRunId = jsonObject.getString("id");
+ String workItemId = new JSONArray(jsonObject.getString("workItemIds")).getString(0);
+
+ boolean success = false;
+
+ String output;
+ for (; true;) {
+
+ try {
+ output = getWorkItems(robotRunId, config);
+ Thread.sleep(5000);
+ if (output.contains("FAILED")) {
+ break;
+ }
+ success = isRobotCompleted(output);
+ if (success) {
+ setResponseUri(output, workItemId, config, robotInputs, audit);
+ break;
+ }
+ } catch (Exception e) {
+ break;
+ }
+ }
+ return success;
+ }
+
+ private String startRobotProcess(List robotInputs, RobotHandlerConfig config) throws JSONException {
+ String formattedInput = buildInput(robotInputs).toString();
+ String uri = getRobotProcessUrl(config) + "/runs";
+ ResponseEntity response = webClient.method(HttpMethod.POST).uri(uri)
+ .header("Authorization", getApiKey()).accept(MediaType.APPLICATION_JSON)
+ .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+ .body(Mono.just(formattedInput), String.class).retrieve().toEntity(String.class).block();
+
+ LOG.error(response.getBody());
+ return response.getBody();
+ }
+
+ private JSONObject buildInput(List robotInputs) throws JSONException {
+ JSONObject formattedInput = new JSONObject();
+ for (int i = 0; i < robotInputs.size(); i++) {
+ formattedInput.put(robotInputs.get(i).getField(), robotInputs.get(i).getValue());
+ }
+ return formattedInput;
+ }
+
+ private String getWorkItems(String robotRunId, RobotHandlerConfig config) {
+ String uri = getRobotProcessUrl(config) + "/runs/" + robotRunId + "/work-items";
+ ResponseEntity response = webClient.method(HttpMethod.GET).uri(uri).header("Authorization", getApiKey())
+ .accept(MediaType.APPLICATION_JSON).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+ .body(Mono.empty(), String.class).retrieve().toEntity(String.class).block();
+
+ return response.getBody();
+ }
+
+ private void setResponseUri(String output, String workItemId, RobotHandlerConfig config,
+ List robotInputs, RobotHandlerAudit audit) throws JSONException, IOException {
+ String jsonData = new JSONObject(output).getString("data");
+ String data = jsonData != null ? new JSONArray(jsonData).getString(0) : null;
+ String files = data != null ? new JSONObject(data).getString("files") : null;
+ String fileIds = files != null ? new JSONArray(files).getString(0) : null;
+ if (fileIds != null) {
+ JSONObject jsonObject = new JSONObject(fileIds);
+ String fileId = jsonObject.getString("id");
+ String fileName = jsonObject.getString("name");
+ String uri = getRobotProcessUrl(config) + "/work-items/" + workItemId + "/files/" + fileId + "/download";
+ audit.setResponseUri(uri);
+ }
+ }
+
+ private boolean isRobotCompleted(String result) {
+ return result.contains("COMPLETED");
+ }
+
+ private String getApiKey() {
+ return this.apiKey;
+ }
+
+ private String getRobotProcessUrl(RobotHandlerConfig config) {
+ return this.processApi + "/" + config.getWorkspaceId() + "/processes/" + config.getProcessId();
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/RobocorpCloudData.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/RobocorpCloudData.java
new file mode 100644
index 00000000..b7b937ff
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/RobocorpCloudData.java
@@ -0,0 +1,16 @@
+package org.camunda.rpa.client.data;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class RobocorpCloudData {
+
+ private String processRunID;
+ private String robotRunId;
+ private String artifactId;
+
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/RobotInput.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/RobotInput.java
new file mode 100644
index 00000000..628a7809
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/RobotInput.java
@@ -0,0 +1,14 @@
+package org.camunda.rpa.client.data;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class RobotInput {
+
+ private String field;
+ private Object value;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/ScriptData.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/ScriptData.java
new file mode 100644
index 00000000..44103d0a
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/ScriptData.java
@@ -0,0 +1,11 @@
+package org.camunda.rpa.client.data;
+
+import lombok.Data;
+
+@Data
+public class ScriptData {
+
+ private String cmd;
+ private String action;
+
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/TaskDataInput.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/TaskDataInput.java
new file mode 100644
index 00000000..7360117c
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/TaskDataInput.java
@@ -0,0 +1,21 @@
+package org.camunda.rpa.client.data;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.camunda.bpm.client.task.ExternalTask;
+
+import java.util.List;
+import java.util.Map;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class TaskDataInput {
+
+ private ExternalTask externalTask;
+
+ private List variableNames;
+
+ private Map additionalVariables;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/constants/ExternalClientConstants.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/constants/ExternalClientConstants.java
new file mode 100644
index 00000000..9e62bc2e
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/constants/ExternalClientConstants.java
@@ -0,0 +1,21 @@
+package org.camunda.rpa.client.data.constants;
+
+/**
+ * File to store the constants
+ */
+public class ExternalClientConstants {
+
+ public static final String FILE_RESPONSE_VAR = "rpaFileResponse";
+
+ public static final String RESPONSE_DIR = "final";
+
+ public static final String OUTPUT_DIR = "output";
+
+ public static final String RUNTIME_DIR = "runtime";
+
+ public static final String ROBOT_CONFIG_FILE = "robot.yaml";
+
+ public static final String ROBOT_OUTPUT_XML_FILE = "output.xml";
+
+ public static final String BACK_SLASH = "/";
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/constants/RobotResponseType.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/constants/RobotResponseType.java
new file mode 100644
index 00000000..28be5ad5
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/constants/RobotResponseType.java
@@ -0,0 +1,12 @@
+package org.camunda.rpa.client.data.constants;
+
+public enum RobotResponseType {
+
+ NONE,
+ FILE,
+ FILE_MULTI,
+ FILE_BASE64_SINGLE,
+ FILE_BASE64_MULTI,
+ KEY_VALUE,
+ SINGLE_VALUE;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/constants/RobotStatus.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/constants/RobotStatus.java
new file mode 100644
index 00000000..899f609e
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/constants/RobotStatus.java
@@ -0,0 +1,8 @@
+package org.camunda.rpa.client.data.constants;
+
+public enum RobotStatus {
+
+ IN_PROGRESS,
+ SUCCESS,
+ FAILED;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/constants/RobotType.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/constants/RobotType.java
new file mode 100644
index 00000000..952bae31
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/constants/RobotType.java
@@ -0,0 +1,10 @@
+package org.camunda.rpa.client.data.constants;
+
+/**
+ * This class is to keep the different types of robots available in the system.
+ */
+public enum RobotType {
+
+ ROBOCORP_RCC,
+ ROBOCORP_CLOUD
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/entity/IEntity.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/entity/IEntity.java
new file mode 100644
index 00000000..3a2fa3a4
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/entity/IEntity.java
@@ -0,0 +1,4 @@
+package org.camunda.rpa.client.data.entity;
+
+public interface IEntity {
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/entity/RobotHandlerAudit.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/entity/RobotHandlerAudit.java
new file mode 100644
index 00000000..572e229e
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/entity/RobotHandlerAudit.java
@@ -0,0 +1,56 @@
+package org.camunda.rpa.client.data.entity;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+import javax.persistence.*;
+
+import org.camunda.rpa.client.data.constants.RobotStatus;
+import org.hibernate.annotations.CreationTimestamp;
+import org.hibernate.annotations.UpdateTimestamp;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author Sneha Suresh
+ *
+ */
+@Entity
+@Table(name = "rpa_client_audit_handler")
+@NoArgsConstructor
+@Data
+public class RobotHandlerAudit implements IEntity, Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+
+ @Enumerated(EnumType.STRING)
+ @Column(name = "status", nullable = false, length = 20)
+ private RobotStatus status;
+
+ @Column(name = "task_id", nullable = false, length = 50)
+ private String taskId;
+
+ @Column(name = "details")
+ private String details;
+
+ //In case of cloud & file response type - this link can be used to download remote response
+ @Column(name = "response_uri")
+ private String responseUri;
+
+ @OneToOne(optional=false)
+ @JoinColumn(name = "handler_id", nullable=false, updatable=false)
+ private RobotHandlerConfig handlerConfig;
+
+ @Column(name = "created_date")
+ @CreationTimestamp
+ private LocalDateTime createdDate;
+
+ @Column(name = "modified_date")
+ @UpdateTimestamp
+ private LocalDateTime modifiedDate;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/entity/RobotHandlerConfig.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/entity/RobotHandlerConfig.java
new file mode 100644
index 00000000..3b0da420
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/entity/RobotHandlerConfig.java
@@ -0,0 +1,79 @@
+package org.camunda.rpa.client.data.entity;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+
+import org.camunda.rpa.client.data.constants.RobotResponseType;
+import org.hibernate.annotations.CreationTimestamp;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author Sneha Suresh
+ *
+ */
+@Entity
+@Table(name = "rpa_client_handler_config")
+@NoArgsConstructor
+@AllArgsConstructor
+@Data
+public class RobotHandlerConfig implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @Column(name = "handler_id")
+ private Integer handlerId;
+
+ @Column(name = "robot_name", length = 250)
+ private String robotName;
+
+ @Column(name = "is_active", nullable = false)
+ private Boolean isActive;
+
+ // topic name of the External Service Task
+ @Column(name = "topic_name", nullable = false)
+ private String topicName;
+
+ // Task file name eg: task.robot / task.py
+ @Column(name = "task_file_name")
+ private String taskFileName;
+
+ // our business logic may need variables
+ @Column(name = "variable_names")
+ private String variableNames;
+
+ // only filter for External Tasks with this process definition key
+ @Column(name = "process_definition_key", nullable = false)
+ private String processDefinitionKey;
+
+ @Enumerated(EnumType.STRING)
+ @Column(name = "response_type", nullable = false, length = 20)
+ private RobotResponseType responseType;
+
+ @Transient
+ private String workingDirName;
+
+ @Column(name = "description", length = 500)
+ private String description;
+
+ @Column(name = "created_date")
+ @CreationTimestamp
+ private LocalDateTime createdDate;
+
+ @Column(name = "workspace_id")
+ private String workspaceId;
+
+ @Column(name = "process_id")
+ private String processId;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/repository/RepoFinder.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/repository/RepoFinder.java
new file mode 100644
index 00000000..4935d0f9
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/repository/RepoFinder.java
@@ -0,0 +1,14 @@
+package org.camunda.rpa.client.data.repository;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Autowired;
+
+@Data
+public class RepoFinder {
+
+ @Autowired
+ private RobotHandlerAuditRepository auditRepository;
+
+ @Autowired
+ private RobotHandlerConfigRepository configRepository;
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/repository/RobotHandlerAuditRepository.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/repository/RobotHandlerAuditRepository.java
new file mode 100644
index 00000000..428e3b77
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/repository/RobotHandlerAuditRepository.java
@@ -0,0 +1,14 @@
+package org.camunda.rpa.client.data.repository;
+
+import org.camunda.rpa.client.data.entity.RobotHandlerAudit;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author Sneha Suresh
+ *
+ */
+@Repository
+public interface RobotHandlerAuditRepository extends CrudRepository {
+
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/repository/RobotHandlerConfigRepository.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/repository/RobotHandlerConfigRepository.java
new file mode 100644
index 00000000..c5efc6b9
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/data/repository/RobotHandlerConfigRepository.java
@@ -0,0 +1,14 @@
+package org.camunda.rpa.client.data.repository;
+
+import org.camunda.rpa.client.data.entity.RobotHandlerConfig;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author Sneha Suresh
+ *
+ */
+@Repository
+public interface RobotHandlerConfigRepository extends CrudRepository {
+
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/FormioIdentityException.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/FormioIdentityException.java
new file mode 100644
index 00000000..3c41c3c6
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/FormioIdentityException.java
@@ -0,0 +1,17 @@
+package org.camunda.rpa.client.exception;
+
+/**
+ * To throw during the identity provider setup / token refresh
+ */
+public class FormioIdentityException extends RuntimeException{
+
+ private static final long serialVersionUID = 1L;
+
+ public FormioIdentityException(String message) {
+ super(message);
+ }
+
+ public FormioIdentityException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/FormioServiceException.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/FormioServiceException.java
new file mode 100644
index 00000000..afa05685
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/FormioServiceException.java
@@ -0,0 +1,17 @@
+package org.camunda.rpa.client.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Specialized exception class for formio calls.
+ *
+ * @author sumathi.thirumani@aot-technologies.com
+ */
+@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+public class FormioServiceException extends RuntimeException {
+
+ public FormioServiceException(String message) {
+ super(message);
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/IdentityProviderException.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/IdentityProviderException.java
new file mode 100644
index 00000000..2582e671
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/IdentityProviderException.java
@@ -0,0 +1,17 @@
+package org.camunda.rpa.client.exception;
+
+/**
+ * To throw during the identity provider setup / token refresh
+ */
+public class IdentityProviderException extends RuntimeException{
+
+ private static final long serialVersionUID = 1L;
+
+ public IdentityProviderException(String message) {
+ super(message);
+ }
+
+ public IdentityProviderException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/RobotClientDataException.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/RobotClientDataException.java
new file mode 100644
index 00000000..aebf6c81
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/RobotClientDataException.java
@@ -0,0 +1,11 @@
+package org.camunda.rpa.client.exception;
+
+/**
+ * Data exception for robot client
+ */
+public class RobotClientDataException extends RuntimeException{
+
+ public RobotClientDataException(String message){
+ super(message);
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/RobotClientRuntimeException.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/RobotClientRuntimeException.java
new file mode 100644
index 00000000..f008afd4
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/exception/RobotClientRuntimeException.java
@@ -0,0 +1,16 @@
+package org.camunda.rpa.client.exception;
+
+import lombok.Data;
+
+/**
+ * To throw during the robot runtime
+ */
+@Data
+public class RobotClientRuntimeException extends RuntimeException {
+ private Integer status;
+
+ public RobotClientRuntimeException(String message, Integer status) {
+ super(message);
+ this.status = status;
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/IRobotHandler.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/IRobotHandler.java
new file mode 100644
index 00000000..5b1739ba
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/IRobotHandler.java
@@ -0,0 +1,32 @@
+package org.camunda.rpa.client.handlers;
+
+import org.camunda.bpm.client.task.ExternalTaskService;
+import org.camunda.rpa.client.data.RobotInput;
+import org.camunda.rpa.client.data.entity.RobotHandlerAudit;
+import org.camunda.rpa.client.data.entity.RobotHandlerConfig;
+import org.camunda.bpm.client.task.ExternalTask;
+import org.camunda.bpm.engine.variable.VariableMap;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * @author Shibin Thomas
+ *
+ */
+public interface IRobotHandler extends TaskHandler{
+
+ RobotHandlerAudit startAudit(ExternalTask externalTask);
+ List buildInputWithAdditionalVariables(ExternalTask externalTask, RobotHandlerConfig config, Map additionalVariables);
+ List buildInput(ExternalTask externalTask, RobotHandlerConfig config);
+ boolean runRobot(List robotInputs, RobotHandlerAudit audit);
+ VariableMap collectResponse(RobotHandlerAudit robotHandlerAudit, String formUrl);
+ void completeAudit(RobotHandlerAudit audit, boolean status, String errorDetails);
+ void handleFailure(ExternalTask externalTask, ExternalTaskService externalTaskService, RobotHandlerAudit robotHandlerAudit, String message);
+ void doCleanup(RobotHandlerAudit robotHandlerAudit);
+
+ default Integer getHandlerId(){
+ return -1;
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/TaskHandler.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/TaskHandler.java
new file mode 100644
index 00000000..3fb1a7af
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/TaskHandler.java
@@ -0,0 +1,8 @@
+package org.camunda.rpa.client.handlers;
+
+import org.camunda.bpm.client.task.ExternalTask;
+import org.camunda.bpm.client.task.ExternalTaskService;
+
+public interface TaskHandler {
+ void subscribe(ExternalTask externalTask, ExternalTaskService externalTaskService);
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/TaskHandlerManager.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/TaskHandlerManager.java
new file mode 100644
index 00000000..58e636bd
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/TaskHandlerManager.java
@@ -0,0 +1,93 @@
+package org.camunda.rpa.client.handlers;
+
+import org.camunda.bpm.client.task.ExternalTask;
+import org.camunda.bpm.client.task.ExternalTaskHandler;
+import org.camunda.bpm.client.task.ExternalTaskService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+public abstract class TaskHandlerManager implements ExternalTaskHandler,TaskHandler {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TaskHandlerManager.class);
+
+ private static final Map activeTaskIdMap = new HashMap<>();
+
+ /**
+ * Not a function to override
+ * @param externalTask
+ * @param externalTaskService
+ */
+ @Override
+ public void execute(ExternalTask externalTask, ExternalTaskService externalTaskService) {
+ String taskId = externalTask.getId();
+ String topicName = externalTask.getTopicName();
+
+ // Allowing multiple robots to run in parallel in the starting will create installation chaos
+ if(activeTaskIdMap.containsKey(topicName)){
+ LOG.info("Active tasks running for topicName :"+topicName);
+ return;
+ } else {
+ LOG.debug("Locking topicName = "+topicName+" for installation");
+ activeTaskIdMap.put(topicName, taskId);
+ }
+
+ Thread taskThread = new Thread(() -> {
+ try {
+ LOG.debug("START: Handler will be invoked to run robots");
+ this.subscribe(externalTask, externalTaskService);
+ LOG.debug("END: Handler will be invoked to run robots");
+ } finally {
+ activeTaskIdMap.remove(topicName);
+ LOG.info("Removed the taskId = "+taskId+", topicName :"+topicName);
+ }
+ });
+ taskThread.start();
+ }
+
+ /**
+ * Not a function to override
+ * @param externalTask
+ * @param externalTaskService
+ */
+ /*@Override
+ public void execute(ExternalTask externalTask, ExternalTaskService externalTaskService) {
+ String taskId = externalTask.getId();
+ String topicName = externalTask.getTopicName();
+
+ // Allowing multiple robots to run in parallel in the starting will create installation chaos
+ int limit = (installLock.containsKey(topicName) && !installLock.get(topicName)) ? 2 : 0;
+ if(activeTaskIdMap.containsKey(topicName)){
+ List taskIds = activeTaskIdMap.get(topicName);
+ LOG.info("Active tasks running for topicName :"+topicName+" is "+taskIds.size());
+ if (taskIds.contains(taskId) || taskIds.size() > limit){
+ return;
+ }
+ } else {
+ LOG.debug("Locking topicName = "+topicName+" for installation");
+ installLock.put(topicName, true);
+ activeTaskIdMap.put(topicName, new ArrayList<>());
+ }
+
+ List taskIds = activeTaskIdMap.get(topicName);
+ if (!taskIds.contains(taskId) && taskIds.size() <= limit) {
+ LOG.info("New tak id added for processing " + taskId+" for topic - "+topicName);
+ taskIds.add(taskId);
+ Thread taskThread = new Thread(() -> {
+ try {
+ LOG.debug("START: Handler will be invoked to run robots");
+ this.subscribe(externalTask, externalTaskService);
+ LOG.debug("END: Handler will be invoked to run robots");
+ } finally {
+ taskIds.remove(taskId);
+ if(installLock.get(topicName)) {
+ installLock.put(topicName, false);
+ }
+ LOG.info("Removed the taskId = "+taskId+", active tasks running for topicName :"+topicName+" is "+taskIds.size());
+ }
+ });
+ taskThread.start();
+ }
+ }*/
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/impl/BackgroundCheckRobotHandler.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/impl/BackgroundCheckRobotHandler.java
new file mode 100644
index 00000000..1e3bfb00
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/impl/BackgroundCheckRobotHandler.java
@@ -0,0 +1,178 @@
+package org.camunda.rpa.client.handlers.impl;
+
+import org.camunda.rpa.client.core.RobotDataManager;
+import org.camunda.rpa.client.data.RobotInput;
+import org.camunda.rpa.client.core.RobotIOManager;
+import org.camunda.rpa.client.core.RobotManager;
+import org.camunda.rpa.client.data.entity.RobotHandlerAudit;
+import org.camunda.rpa.client.data.entity.RobotHandlerConfig;
+
+import org.camunda.bpm.client.spring.annotation.ExternalTaskSubscription;
+import org.camunda.bpm.client.task.ExternalTask;
+import org.camunda.bpm.client.task.ExternalTaskService;
+import org.camunda.bpm.engine.variable.VariableMap;
+import org.camunda.rpa.client.handlers.IRobotHandler;
+import org.camunda.rpa.client.handlers.TaskHandlerManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class is responsible to handle the background-check robot
+ *
+ * @author Shibin Thomas
+ *
+ */
+@Component
+@ExternalTaskSubscription("background-check")
+public class BackgroundCheckRobotHandler extends TaskHandlerManager implements IRobotHandler {
+
+ private static final Logger LOG = LoggerFactory.getLogger(BackgroundCheckRobotHandler.class);
+
+ private static final Integer HANDLER_ID = 1;
+
+ @Autowired
+ private RobotIOManager robotIOManager;
+
+ @Autowired
+ private RobotDataManager robotDataManager;
+
+ @Autowired
+ private RobotManager robotManager;
+
+ /**
+ * This method is used to manage and run the robot
+ *
+ * @param externalTask
+ * @param externalTaskService
+ *
+ */
+ @Override
+ public void subscribe(ExternalTask externalTask, ExternalTaskService externalTaskService) {
+
+ LOG.debug("START - executing background check service");
+
+ RobotHandlerAudit robotHandlerAudit = startAudit(externalTask);
+
+ try {
+ Map additionalVariables = new HashMap<>();
+ additionalVariables.put("working-dir", robotHandlerAudit.getHandlerConfig().getWorkingDirName());
+
+ List robotInputs = buildInputWithAdditionalVariables(externalTask,
+ robotHandlerAudit.getHandlerConfig(), additionalVariables);
+
+ boolean status = runRobot(robotInputs, robotHandlerAudit);
+
+ VariableMap variableMap = null;
+ if(status) {
+ variableMap = collectResponse(robotHandlerAudit, externalTask.getVariable("formUrl"));
+ }
+ externalTaskService.complete(externalTask, variableMap);
+
+ completeAudit(robotHandlerAudit, true, null);
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ handleFailure(externalTask, externalTaskService, robotHandlerAudit, ex.getMessage());
+ } finally {
+ doCleanup(robotHandlerAudit);
+ }
+
+ LOG.debug("END - executing background check service");
+ }
+
+ /**
+ * Create a new entry in the Audit table to acknowledge that the robot started successfully.
+ * This method invokes initAudit() to update the robot current status
+ * @param externalTask
+ *
+ */
+ @Override
+ public RobotHandlerAudit startAudit(ExternalTask externalTask) {
+ return robotDataManager.initAudit(externalTask, getHandlerId());
+ }
+
+ /**
+ * This method will build input with additional variables for robot by invoking buildInput() method.
+ * @param externalTask
+ * @param config
+ * @param additionalVariables
+ *
+ */
+ @Override
+ public List buildInputWithAdditionalVariables(ExternalTask externalTask, RobotHandlerConfig config,
+ Map additionalVariables) {
+ return robotIOManager.buildInput(externalTask, config, additionalVariables);
+ }
+
+ /**
+ * This is an overriden method used to build the Input.
+ * @param externalTask
+ * @param config
+ *
+ */
+ @Override
+ public List buildInput(ExternalTask externalTask, RobotHandlerConfig config) {
+
+ return null;
+ }
+
+ /**
+ * This method invokes runRobot() to find out the robot type, build a script with input parameters and run using script manager
+ * @param robotInputs
+ * @param audit
+ *
+ */
+ @Override
+ public boolean runRobot(List robotInputs, RobotHandlerAudit audit) {
+ return robotManager.runRobot(robotInputs, audit);
+ }
+
+ /**
+ * Once robot completed its task successfully, this method invokes and collect the response.
+ * @param robotHandlerAudit
+ * @return
+ *
+ */
+ @Override
+ public VariableMap collectResponse(RobotHandlerAudit robotHandlerAudit, String formUrl) {
+ return robotIOManager.getOutputData(robotHandlerAudit, formUrl);
+ }
+
+ /**
+ * Complete the audit process and update the status in audit table
+ * @param audit
+ * @param status
+ * @param errorDetails
+ *
+ */
+ @Override
+ public void completeAudit(RobotHandlerAudit audit, boolean status, String errorDetails) {
+ robotDataManager.finalizeAudit(audit, status, errorDetails);
+ }
+
+ @Override
+ public void handleFailure(ExternalTask externalTask, ExternalTaskService externalTaskService, RobotHandlerAudit robotHandlerAudit, String message) {
+ completeAudit(robotHandlerAudit, false, message);
+ externalTaskService.handleFailure(externalTask, message, message, 0, 0);
+ }
+
+ @Override
+ public void doCleanup(RobotHandlerAudit robotHandlerAudit) {
+ robotIOManager.clearAndRemoveOutputDirectory(robotHandlerAudit);
+ }
+
+ /**
+ * @return
+ *
+ */
+ @Override
+ public Integer getHandlerId() {
+ return HANDLER_ID;
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/impl/WebScraperRobotHandler.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/impl/WebScraperRobotHandler.java
new file mode 100644
index 00000000..e2e880b1
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/handlers/impl/WebScraperRobotHandler.java
@@ -0,0 +1,172 @@
+package org.camunda.rpa.client.handlers.impl;
+
+import org.camunda.bpm.client.spring.annotation.ExternalTaskSubscription;
+import org.camunda.bpm.client.task.ExternalTask;
+import org.camunda.bpm.client.task.ExternalTaskService;
+import org.camunda.bpm.engine.variable.VariableMap;
+import org.camunda.rpa.client.core.RobotDataManager;
+import org.camunda.rpa.client.core.RobotIOManager;
+import org.camunda.rpa.client.core.RobotManager;
+import org.camunda.rpa.client.data.RobotInput;
+import org.camunda.rpa.client.data.entity.RobotHandlerAudit;
+import org.camunda.rpa.client.data.entity.RobotHandlerConfig;
+import org.camunda.rpa.client.handlers.IRobotHandler;
+import org.camunda.rpa.client.handlers.TaskHandlerManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class is responsible to handle the web-scraper robot
+ *
+ * @author Shibin Thomas
+ *
+ */
+@Component
+@ExternalTaskSubscription("web-scraper")
+public class WebScraperRobotHandler extends TaskHandlerManager implements IRobotHandler {
+
+ private static final Logger LOG = LoggerFactory.getLogger(WebScraperRobotHandler.class);
+
+ private static final Integer HANDLER_ID = 2;
+
+ @Autowired
+ private RobotIOManager robotIOManager;
+
+ @Autowired
+ private RobotDataManager robotDataManager;
+
+ @Autowired
+ private RobotManager robotManager;
+
+ /**
+ * This method is used to manage and run the robot
+ *
+ * @param externalTask
+ * @param externalTaskService
+ *
+ */
+ @Override
+ public void subscribe(ExternalTask externalTask, ExternalTaskService externalTaskService) {
+ LOG.debug("START - executing web-scraper service");
+
+ RobotHandlerAudit robotHandlerAudit = startAudit(externalTask);
+
+ try {
+ Map additionalVariables = new HashMap<>();
+ additionalVariables.put("working-dir", robotHandlerAudit.getHandlerConfig().getWorkingDirName());
+
+ List robotInputs = buildInputWithAdditionalVariables(externalTask,
+ robotHandlerAudit.getHandlerConfig(), additionalVariables);
+
+ boolean status = runRobot(robotInputs, robotHandlerAudit);
+
+ VariableMap variableMap = null;
+ if(status) {
+ variableMap = collectResponse(robotHandlerAudit, externalTask.getVariable("formUrl"));
+ }
+ externalTaskService.complete(externalTask, variableMap);
+
+ completeAudit(robotHandlerAudit, true, null);
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ handleFailure(externalTask, externalTaskService, robotHandlerAudit, ex.getMessage());
+ } finally {
+ doCleanup(robotHandlerAudit);
+ }
+
+ LOG.debug("END - executing web scraper service");
+ }
+
+ /**
+ * Create a new entry in the Audit table to acknowledge that the robot started successfully.
+ * This method invokes initAudit() to update the robot current status
+ * @param externalTask
+ *
+ */
+ @Override
+ public RobotHandlerAudit startAudit(ExternalTask externalTask) {
+ return robotDataManager.initAudit(externalTask, getHandlerId());
+ }
+
+ /**
+ * This method will build input with additional variables for robot by invoking buildInput() method.
+ * @param externalTask
+ * @param config
+ * @param additionalVariables
+ *
+ */
+ @Override
+ public List buildInputWithAdditionalVariables(ExternalTask externalTask, RobotHandlerConfig config,
+ Map additionalVariables) {
+ return robotIOManager.buildInput(externalTask, config, additionalVariables);
+ }
+
+ /**
+ * This is an overriden method used to build the Input.
+ * @param externalTask
+ * @param config
+ *
+ */
+ @Override
+ public List buildInput(ExternalTask externalTask, RobotHandlerConfig config) {
+
+ return null;
+ }
+
+ /**
+ * This method invokes runRobot() to find out the robot type, build a script with input parameters and run using script manager
+ * @param robotInputs
+ * @param audit
+ *
+ */
+ @Override
+ public boolean runRobot(List robotInputs, RobotHandlerAudit audit) {
+ return robotManager.runRobot(robotInputs, audit);
+ }
+
+ /**
+ * Once robot completed its task successfully, this method invokes and collect the response.
+ * @param robotHandlerAudit
+ * @return
+ *
+ */
+ @Override
+ public VariableMap collectResponse(RobotHandlerAudit robotHandlerAudit, String formUrl) {
+ return robotIOManager.getOutputData(robotHandlerAudit, formUrl);
+ }
+
+ /**
+ * Complete the audit process and update the status in audit table
+ * @param audit
+ * @param status
+ * @param errorDetails
+ *
+ */
+ @Override
+ public void completeAudit(RobotHandlerAudit audit, boolean status, String errorDetails) {
+ robotDataManager.finalizeAudit(audit, status, errorDetails);
+ }
+
+ @Override
+ public void handleFailure(ExternalTask externalTask, ExternalTaskService externalTaskService, RobotHandlerAudit robotHandlerAudit, String message) {
+ completeAudit(robotHandlerAudit, false, message);
+ externalTaskService.handleFailure(externalTask, message, message, 0, 0);
+ }
+
+ @Override
+ public void doCleanup(RobotHandlerAudit robotHandlerAudit) {
+ robotIOManager.clearAndRemoveOutputDirectory(robotHandlerAudit);
+ }
+
+ @Override
+ public Integer getHandlerId() {
+ return HANDLER_ID;
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/util/ObjectUtil.java b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/util/ObjectUtil.java
new file mode 100644
index 00000000..0ef5f00e
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/java/org/camunda/rpa/client/util/ObjectUtil.java
@@ -0,0 +1,31 @@
+package org.camunda.rpa.client.util;
+
+import java.util.Map;
+
+/**
+ * author : Shibin Thomas
+ */
+public class ObjectUtil {
+
+ /**
+ *
+ * @param entry
+ * @param key
+ * @return
+ */
+ public static String convertKeyToString(Map entry, String key){
+ if(entry == null) return null;
+ return entry.containsKey(key)?entry.get(key).toString():null;
+ }
+
+ /**
+ *
+ * @param entry
+ * @param key
+ * @return
+ */
+ public static Long convertKeyToLong(Map entry, String key){
+ if(entry == null) return 0L;
+ return entry.containsKey(key)?Long.parseLong(String.valueOf(entry.get(key))):0L;
+ }
+}
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/application.yaml b/rpa-robocorp-extention/external-client-extention/src/main/resources/application.yaml
new file mode 100644
index 00000000..79006435
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/application.yaml
@@ -0,0 +1,62 @@
+# Set variables
+
+keycloak.url: ${KEYCLOAK_URL}
+keycloak.url.realm: ${KEYCLOAK_URL_REALM}
+keycloak.clientId: ${KEYCLOAK_CLIENTID}
+keycloak.clientSecret: ${KEYCLOAK_CLIENTSECRET}
+apiKey: ${ROBOCORP_CLOUD_APIKEY}
+
+spring:
+ datasource:
+ url: ${RPA_JDBC_URL:jdbc:h2:./camunda-rpa-client-db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE}
+ username: ${RPA_JDBC_USER:sa}
+ password: ${RPA_JDBC_PASSWORD:sa}
+ driverClassName: ${RPA_JDBC_DRIVER:org.h2.Driver}
+ type: com.zaxxer.hikari.HikariDataSource
+ connectionTimeout: ${RPA_HIKARI_CONN_TIMEOUT:30000}
+ idleTimeout: ${RPA_HIKARI_IDLE_TIMEOUT:600000}
+ maximumPoolSize: ${RPA_HIKARI_MAX_POOLSIZE:10}
+ validationTimeout: ${RPA_HIKARI_VALID_TIMEOUT:5000}
+ liquibase:
+ change-log: classpath:/db/changelog/db-changelog-master.xml
+ webclient:
+ max-buffer-size: 20
+
+formsflow:
+ formio:
+ enabled: ${FORMIO_ENABLED:true}
+ url: ${FORMIO_URL}
+ security:
+ access-token-uri: ${FORMIO_URL}/user/login
+ username: ${FORMIO_USERNAME}
+ password: ${FORMIO_PASSWORD}
+
+robot:
+ base-dir: ${ROBOT_BASE_DIR:}
+ robot-type: ${ROBOT_TYPE:ROBOCORP_RCC}
+ cloud:
+ api-url: ${ROBOCORP_CLOUD_BASE_URL}/process-v1/workspaces
+ api-key: ${apiKey}
+
+
+client:
+ base-url: ${CAMUNDA_BPM_URL}/camunda/engine-rest # the URL pointing to the Camunda Platform Runtime REST API
+ lock-duration: ${LOCK_DURATION:3000} # defines how many milliseconds the External Tasks are locked until they can be fetched again
+ max-tasks: ${MAX_TASKS:10}
+ disable-backoff-strategy: true
+ async-response-timeout: 10000
+ auth:
+ client-id: ${keycloak.clientId}
+ client-secret: ${keycloak.clientSecret}
+ token-uri: ${keycloak.url}/auth/realms/${keycloak.url.realm}/protocol/openid-connect/token
+ grant_type: client_credentials
+
+server:
+ port: 8090
+
+logging:
+ level:
+ org.springframework.security: ${RPA_APP_LOG_LEVEL:error}
+ org.springframework.jdbc: ${RPA_APP_LOG_LEVEL:error}
+ org.camunda.bpm.client: ${RPA_APP_LOG_LEVEL:error}
+ org.camunda.rpa.client: ${RPA_APP_LOG_LEVEL:error}
\ No newline at end of file
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/db/changelog/db-changelog-master.xml b/rpa-robocorp-extention/external-client-extention/src/main/resources/db/changelog/db-changelog-master.xml
new file mode 100644
index 00000000..cef6aab3
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/db/changelog/db-changelog-master.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ insert into rpa_client_handler_config values(1,
+ 'background-check', true, 'FILE', 'background-check', 'tasks.robot',
+ 'businessOperatingName,businessWebsite,formUrl',
+ 'two-step-approval-with-rpa', 'New Business Background checker
+ robot', CURRENT_DATE, '92ac4b6f-a891-4bab-9b8c-c3022d88d690',
+ 'a52dd66a-fe47-4db7-aa3e-769ad09a83b4')
+
+ insert into rpa_client_handler_config values(2, 'web-scraper',
+ true, 'FILE', 'web-scraper', 'tasks.robot',
+ 'organizationName,formUrl', 'onestepapprovalwithrpa', 'Freedom Of
+ Information Background checker robot', CURRENT_DATE,
+ '92ac4b6f-a891-4bab-9b8c-c3022d88d690',
+ 'a52dd66a-fe47-4db7-aa3e-769ad09a83b4')
+
+
+
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/chromedriver.log b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/chromedriver.log
new file mode 100644
index 00000000..4c71a3e9
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/chromedriver.log
@@ -0,0 +1,1212 @@
+[1642768233.604][INFO]: Starting ChromeDriver 97.0.4692.36 (747e0a0f19c13ca6ee136200f5b097448ae4892f-refs/branch-heads/4692@{#607}) on port 63985
+[1642768233.604][INFO]: Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
+[1642768234.129][INFO]: [7877b0b61dac0daae69a230280b72e8a] COMMAND InitSession {
+ "capabilities": {
+ "alwaysMatch": {
+ "browserName": "chrome",
+ "goog:chromeOptions": {
+ "args": [ "--disable-dev-shm-usage", "--disable-web-security", "--allow-running-insecure-content", "--no-sandbox" ],
+ "excludeSwitches": [ "enable-logging", "enable-automation" ],
+ "extensions": [ ],
+ "prefs": {
+ "credentials_enable_service": false,
+ "profile.password_manager_enabled": false,
+ "safebrowsing.enabled": true
+ }
+ },
+ "platformName": "any"
+ },
+ "firstMatch": [ {
+
+ } ]
+ },
+ "desiredCapabilities": {
+ "browserName": "chrome",
+ "goog:chromeOptions": {
+ "args": [ "--disable-dev-shm-usage", "--disable-web-security", "--allow-running-insecure-content", "--no-sandbox" ],
+ "excludeSwitches": [ "enable-logging", "enable-automation" ],
+ "extensions": [ ],
+ "prefs": {
+ "credentials_enable_service": false,
+ "profile.password_manager_enabled": false,
+ "safebrowsing.enabled": true
+ }
+ },
+ "platform": "ANY",
+ "version": ""
+ }
+}
+[1642768234.135][INFO]: Populating Preferences file: {
+ "alternate_error_pages": {
+ "enabled": false
+ },
+ "autofill": {
+ "enabled": false
+ },
+ "browser": {
+ "check_default_browser": false
+ },
+ "credentials_enable_service": false,
+ "distribution": {
+ "import_bookmarks": false,
+ "import_history": false,
+ "import_search_engine": false,
+ "make_chrome_default_for_user": false,
+ "skip_first_run_ui": true
+ },
+ "dns_prefetching": {
+ "enabled": false
+ },
+ "profile": {
+ "content_settings": {
+ "pattern_pairs": {
+ "https://*,*": {
+ "media-stream": {
+ "audio": "Default",
+ "video": "Default"
+ }
+ }
+ }
+ },
+ "default_content_setting_values": {
+ "geolocation": 1
+ },
+ "default_content_settings": {
+ "geolocation": 1,
+ "mouselock": 1,
+ "notifications": 1,
+ "popups": 1,
+ "ppapi-broker": 1
+ },
+ "password_manager_enabled": false
+ },
+ "safebrowsing": {
+ "enabled": true
+ },
+ "search": {
+ "suggest_enabled": false
+ },
+ "translate": {
+ "enabled": false
+ }
+}
+[1642768234.136][INFO]: Populating Local State file: {
+ "background_mode": {
+ "enabled": false
+ },
+ "ssl": {
+ "rev_checking": {
+ "enabled": false
+ }
+ }
+}
+[1642768234.139][INFO]: Launching chrome: "C:\Program Files\Google\Chrome\Application\chrome.exe" --allow-pre-commit-input --allow-running-insecure-content --disable-background-networking --disable-backgrounding-occluded-windows --disable-client-side-phishing-detection --disable-default-apps --disable-dev-shm-usage --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --disable-web-security --enable-blink-features=ShadowDOMV0 --log-level=0 --no-first-run --no-sandbox --no-service-autorun --password-store=basic --remote-debugging-port=0 --test-type=webdriver --use-mock-keychain --user-data-dir="C:\Users\DELL\AppData\Local\robocorp\temp\a68bc07d8def94b9\scoped_dir3980_150512762" data:,
+[1642768234.462][DEBUG]: DevTools HTTP Request: http://localhost:63991/json/version
+[1642768234.786][DEBUG]: DevTools HTTP Response: {
+ "Browser": "Chrome/97.0.4692.71",
+ "Protocol-Version": "1.3",
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36",
+ "V8-Version": "9.7.106.18",
+ "WebKit-Version": "537.36 (@adefa7837d02a07a604c1e6eff0b3a09422ab88d)",
+ "webSocketDebuggerUrl": "ws://localhost:63991/devtools/browser/d67df48b-dec4-470a-86c2-03495a66c009"
+}
+
+[1642768234.787][DEBUG]: DevTools HTTP Request: http://localhost:63991/json/list
+[1642768234.789][DEBUG]: DevTools HTTP Response: [ {
+ "description": "",
+ "devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:63991/devtools/page/EBDCB326EE7911F3266EA857DCFF7A23",
+ "id": "EBDCB326EE7911F3266EA857DCFF7A23",
+ "title": "data:,",
+ "type": "page",
+ "url": "data:,",
+ "webSocketDebuggerUrl": "ws://localhost:63991/devtools/page/EBDCB326EE7911F3266EA857DCFF7A23"
+} ]
+
+[1642768234.789][DEBUG]: DevTools HTTP Request: http://localhost:63991/json/list
+[1642768234.792][DEBUG]: DevTools HTTP Response: [ {
+ "description": "",
+ "devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:63991/devtools/page/EBDCB326EE7911F3266EA857DCFF7A23",
+ "id": "EBDCB326EE7911F3266EA857DCFF7A23",
+ "title": "data:,",
+ "type": "page",
+ "url": "data:,",
+ "webSocketDebuggerUrl": "ws://localhost:63991/devtools/page/EBDCB326EE7911F3266EA857DCFF7A23"
+} ]
+
+[1642768234.801][INFO]: resolved localhost to ["::1","127.0.0.1"]
+[1642768234.804][DEBUG]: DevTools WebSocket Command: Page.addScriptToEvaluateOnNewDocument (id=1) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "source": "(function () {window.cdc_adoQpoasnfa76pfcZLmcfl_Array = window.Array;window.cdc_adoQpoasnfa76pfcZLmcfl_Promise = window.Promise;window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol = window.Symbol;}) ();"
+}
+[1642768234.804][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=2) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "(function () {window.cdc_adoQpoasnfa76pfcZLmcfl_Array = window.Array;window.cdc_adoQpoasnfa76pfcZLmcfl_Promise = window.Promise;window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol = window.Symbol;}) ();"
+}
+[1642768234.804][DEBUG]: DevTools WebSocket Command: Log.enable (id=3) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.804][DEBUG]: DevTools WebSocket Command: DOM.getDocument (id=4) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.804][DEBUG]: DevTools WebSocket Command: Target.setAutoAttach (id=5) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "autoAttach": true,
+ "flatten": true,
+ "waitForDebuggerOnStart": false
+}
+[1642768234.804][DEBUG]: DevTools WebSocket Command: Page.enable (id=6) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.804][DEBUG]: DevTools WebSocket Command: Page.enable (id=7) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.805][DEBUG]: DevTools WebSocket Response: Page.addScriptToEvaluateOnNewDocument (id=1) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "identifier": "1"
+}
+[1642768234.805][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=2) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "type": "undefined"
+ }
+}
+[1642768234.805][DEBUG]: DevTools WebSocket Response: Log.enable (id=3) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.805][DEBUG]: DevTools WebSocket Response: DOM.getDocument (id=4) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "root": {
+ "backendNodeId": 1,
+ "baseURL": "data:,",
+ "childNodeCount": 1,
+ "children": [ {
+ "attributes": [ ],
+ "backendNodeId": 2,
+ "childNodeCount": 2,
+ "children": [ {
+ "attributes": [ ],
+ "backendNodeId": 3,
+ "childNodeCount": 0,
+ "localName": "head",
+ "nodeId": 3,
+ "nodeName": "HEAD",
+ "nodeType": 1,
+ "nodeValue": "",
+ "parentId": 2
+ }, {
+ "attributes": [ ],
+ "backendNodeId": 4,
+ "childNodeCount": 0,
+ "localName": "body",
+ "nodeId": 4,
+ "nodeName": "BODY",
+ "nodeType": 1,
+ "nodeValue": "",
+ "parentId": 2
+ } ],
+ "frameId": "EBDCB326EE7911F3266EA857DCFF7A23",
+ "localName": "html",
+ "nodeId": 2,
+ "nodeName": "HTML",
+ "nodeType": 1,
+ "nodeValue": "",
+ "parentId": 1
+ } ],
+ "compatibilityMode": "NoQuirksMode",
+ "documentURL": "data:,",
+ "localName": "",
+ "nodeId": 1,
+ "nodeName": "#document",
+ "nodeType": 9,
+ "nodeValue": "",
+ "xmlVersion": ""
+ }
+}
+[1642768234.805][DEBUG]: DevTools WebSocket Response: Target.setAutoAttach (id=5) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.805][DEBUG]: DevTools WebSocket Response: Page.enable (id=6) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.805][DEBUG]: DevTools WebSocket Response: Page.enable (id=7) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.805][DEBUG]: DevTools WebSocket Command: Runtime.enable (id=8) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.805][DEBUG]: DevTools WebSocket Event: Runtime.executionContextCreated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "context": {
+ "auxData": {
+ "frameId": "EBDCB326EE7911F3266EA857DCFF7A23",
+ "isDefault": true,
+ "type": "default"
+ },
+ "id": 1,
+ "name": "",
+ "origin": "://",
+ "uniqueId": "-5811666141198061506.-5623120982200588376"
+ }
+}
+[1642768234.805][DEBUG]: DevTools WebSocket Response: Runtime.enable (id=8) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.805][DEBUG]: DevTools WebSocket Command: Page.enable (id=9) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.806][DEBUG]: DevTools WebSocket Response: Page.enable (id=9) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.806][DEBUG]: DevTools WebSocket Command: Runtime.enable (id=10) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.809][DEBUG]: DevTools WebSocket Response: Runtime.enable (id=10) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.809][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=11) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "awaitPromise": true,
+ "expression": "(function() { // Copyright (c) 2012 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n/**\n * Enum f...",
+ "returnByValue": true
+}
+[1642768234.814][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=11) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "type": "object",
+ "value": {
+ "status": 0,
+ "value": 1
+ }
+ }
+}
+[1642768234.814][INFO]: [7877b0b61dac0daae69a230280b72e8a] RESPONSE InitSession {
+ "capabilities": {
+ "acceptInsecureCerts": false,
+ "browserName": "chrome",
+ "browserVersion": "97.0.4692.71",
+ "chrome": {
+ "chromedriverVersion": "97.0.4692.36 (747e0a0f19c13ca6ee136200f5b097448ae4892f-refs/branch-heads/4692@{#607})",
+ "userDataDir": "C:\\Users\\DELL\\AppData\\Local\\robocorp\\temp\\a68bc07d8def94b9\\scoped_dir3980_150512762"
+ },
+ "goog:chromeOptions": {
+ "debuggerAddress": "localhost:63991"
+ },
+ "networkConnectionEnabled": false,
+ "pageLoadStrategy": "normal",
+ "platformName": "windows",
+ "proxy": {
+
+ },
+ "setWindowRect": true,
+ "strictFileInteractability": false,
+ "timeouts": {
+ "implicit": 0,
+ "pageLoad": 300000,
+ "script": 30000
+ },
+ "unhandledPromptBehavior": "dismiss and notify",
+ "webauthn:extension:credBlob": true,
+ "webauthn:extension:largeBlob": true,
+ "webauthn:virtualAuthenticators": true
+ },
+ "sessionId": "7877b0b61dac0daae69a230280b72e8a"
+}
+[1642768234.818][INFO]: [7877b0b61dac0daae69a230280b72e8a] COMMAND Navigate {
+ "url": "http://www.gs.com"
+}
+[1642768234.818][INFO]: Waiting for pending navigations...
+[1642768234.818][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=12) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768234.819][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=12) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768234.819][DEBUG]: DevTools WebSocket Command: DOM.getDocument (id=13) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768234.820][DEBUG]: DevTools WebSocket Response: DOM.getDocument (id=13) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "root": {
+ "backendNodeId": 1,
+ "baseURL": "data:,",
+ "childNodeCount": 1,
+ "children": [ {
+ "attributes": [ ],
+ "backendNodeId": 2,
+ "childNodeCount": 2,
+ "children": [ {
+ "attributes": [ ],
+ "backendNodeId": 3,
+ "childNodeCount": 0,
+ "localName": "head",
+ "nodeId": 7,
+ "nodeName": "HEAD",
+ "nodeType": 1,
+ "nodeValue": "",
+ "parentId": 6
+ }, {
+ "attributes": [ ],
+ "backendNodeId": 4,
+ "childNodeCount": 0,
+ "localName": "body",
+ "nodeId": 8,
+ "nodeName": "BODY",
+ "nodeType": 1,
+ "nodeValue": "",
+ "parentId": 6
+ } ],
+ "frameId": "EBDCB326EE7911F3266EA857DCFF7A23",
+ "localName": "html",
+ "nodeId": 6,
+ "nodeName": "HTML",
+ "nodeType": 1,
+ "nodeValue": "",
+ "parentId": 5
+ } ],
+ "compatibilityMode": "NoQuirksMode",
+ "documentURL": "data:,",
+ "localName": "",
+ "nodeId": 5,
+ "nodeName": "#document",
+ "nodeType": 9,
+ "nodeValue": "",
+ "xmlVersion": ""
+ }
+}
+[1642768234.820][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=14) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "awaitPromise": false,
+ "expression": "document.readyState",
+ "returnByValue": true
+}
+[1642768234.821][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=14) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "type": "string",
+ "value": "complete"
+ }
+}
+[1642768234.821][INFO]: Done waiting for pending navigations. Status: ok
+[1642768234.821][DEBUG]: DevTools WebSocket Command: Page.navigate (id=15) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "url": "http://www.gs.com"
+}
+[1642768235.761][DEBUG]: DevTools WebSocket Response: Page.navigate (id=15) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "frameId": "EBDCB326EE7911F3266EA857DCFF7A23",
+ "loaderId": "DE9FABA8ED796A857BB2218290162DC8"
+}
+[1642768235.762][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=16) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "document.URL"
+}
+[1642768235.767][DEBUG]: DevTools WebSocket Event: Runtime.executionContextsCleared EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768235.768][DEBUG]: DevTools WebSocket Event: Page.frameStartedLoading EBDCB326EE7911F3266EA857DCFF7A23 {
+ "frameId": "EBDCB326EE7911F3266EA857DCFF7A23"
+}
+[1642768235.790][DEBUG]: DevTools WebSocket Event: Runtime.executionContextsCleared EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768235.790][DEBUG]: DevTools WebSocket Event: Page.frameNavigated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "frame": {
+ "adFrameStatus": {
+ "adFrameType": "none"
+ },
+ "crossOriginIsolatedContextType": "NotIsolated",
+ "domainAndRegistry": "goldmansachs.com",
+ "gatedAPIFeatures": [ ],
+ "id": "EBDCB326EE7911F3266EA857DCFF7A23",
+ "loaderId": "DE9FABA8ED796A857BB2218290162DC8",
+ "mimeType": "text/html",
+ "secureContextType": "Secure",
+ "securityOrigin": "https://www.goldmansachs.com",
+ "url": "https://www.goldmansachs.com/"
+ },
+ "type": "Navigation"
+}
+[1642768235.790][DEBUG]: DevTools WebSocket Event: Runtime.executionContextCreated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "context": {
+ "auxData": {
+ "frameId": "EBDCB326EE7911F3266EA857DCFF7A23",
+ "isDefault": true,
+ "type": "default"
+ },
+ "id": 1,
+ "name": "",
+ "origin": "https://www.goldmansachs.com",
+ "uniqueId": "-302677859584493654.2588381363366894121"
+ }
+}
+[1642768235.790][DEBUG]: DevTools WebSocket Event: DOM.documentUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768235.790][DEBUG]: DevTools WebSocket Command: DOM.getDocument (id=17) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768235.800][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=16) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "type": "string",
+ "value": "https://www.goldmansachs.com/"
+ }
+}
+[1642768235.800][INFO]: Waiting for pending navigations...
+[1642768235.800][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=18) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768235.802][DEBUG]: DevTools WebSocket Response: DOM.getDocument (id=17) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "root": {
+ "backendNodeId": 1,
+ "baseURL": "https://www.goldmansachs.com/",
+ "childNodeCount": 0,
+ "children": [ ],
+ "compatibilityMode": "NoQuirksMode",
+ "documentURL": "https://www.goldmansachs.com/",
+ "localName": "",
+ "nodeId": 1,
+ "nodeName": "#document",
+ "nodeType": 9,
+ "nodeValue": "",
+ "xmlVersion": ""
+ }
+}
+[1642768235.802][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=18) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768235.817][DEBUG]: DevTools WebSocket Event: DOM.childNodeInserted EBDCB326EE7911F3266EA857DCFF7A23 {
+ "node": {
+ "backendNodeId": 2,
+ "localName": "",
+ "nodeId": 2,
+ "nodeName": "html",
+ "nodeType": 10,
+ "nodeValue": "",
+ "publicId": "",
+ "systemId": ""
+ },
+ "parentNodeId": 1,
+ "previousNodeId": 0
+}
+[1642768235.817][DEBUG]: DevTools WebSocket Event: DOM.childNodeInserted EBDCB326EE7911F3266EA857DCFF7A23 {
+ "node": {
+ "attributes": [ "lang", "en-US", "itemscope", "", "itemtype", "http://schema.org/Article" ],
+ "backendNodeId": 3,
+ "childNodeCount": 0,
+ "frameId": "EBDCB326EE7911F3266EA857DCFF7A23",
+ "localName": "html",
+ "nodeId": 3,
+ "nodeName": "HTML",
+ "nodeType": 1,
+ "nodeValue": ""
+ },
+ "parentNodeId": 1,
+ "previousNodeId": 2
+}
+[1642768235.817][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 1,
+ "nodeId": 3
+}
+[1642768235.817][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=19) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768235.823][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=19) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768236.169][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 2,
+ "nodeId": 3
+}
+[1642768236.169][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 1,
+ "nodeId": 3
+}
+[1642768236.169][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 2,
+ "nodeId": 3
+}
+[1642768236.169][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 1,
+ "nodeId": 3
+}
+[1642768236.169][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": " js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video"
+}
+[1642768236.169][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": " js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder"
+}
+[1642768236.169][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 2,
+ "nodeId": 3
+}
+[1642768236.169][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 1,
+ "nodeId": 3
+}
+[1642768236.169][DEBUG]: DevTools WebSocket Event: Log.entryAdded EBDCB326EE7911F3266EA857DCFF7A23 {
+ "entry": {
+ "level": "warning",
+ "lineNumber": 362,
+ "source": "security",
+ "stackTrace": {
+ "callFrames": [ {
+ "columnNumber": 90,
+ "functionName": "Pb",
+ "lineNumber": 362,
+ "scriptId": "12",
+ "url": "https://www.goldmansachs.com/a/pgs/js/prod/gsAll.js"
+ }, {
+ "columnNumber": 380,
+ "functionName": "GS.Sitewide",
+ "lineNumber": 362,
+ "scriptId": "12",
+ "url": "https://www.goldmansachs.com/a/pgs/js/prod/gsAll.js"
+ }, {
+ "columnNumber": 468,
+ "functionName": "",
+ "lineNumber": 463,
+ "scriptId": "12",
+ "url": "https://www.goldmansachs.com/a/pgs/js/prod/gsAll.js"
+ }, {
+ "columnNumber": 484,
+ "functionName": "",
+ "lineNumber": 463,
+ "scriptId": "12",
+ "url": "https://www.goldmansachs.com/a/pgs/js/prod/gsAll.js"
+ } ]
+ },
+ "text": "Mixed Content: The page at 'https://www.goldmansachs.com/' was loaded over HTTPS, but requested an insecure script 'http://home.web.gs.com/inettag.js'. This content should also be served over HTTPS.",
+ "timestamp": 1.642768236168736e+12,
+ "url": "https://www.goldmansachs.com/a/pgs/js/prod/gsAll.js"
+ }
+}
+[1642768236.169][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=20) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768236.175][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading"
+}
+[1642768236.180][DEBUG]: DevTools WebSocket Event: DOM.inlineStyleInvalidated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "nodeIds": [ 3 ]
+}
+[1642768236.180][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=20) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading wf-universltw0147lightcn-n4-loading"
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 2,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 1,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 2,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 1,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 2,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 3,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading wf-universltw0147lightcn-n4-loading wf-universltw0155oblique-n4-loading"
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 4,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 3,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 4,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 3,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 4,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 5,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading wf-universltw0147lightcn-n4-loading wf-universltw0155oblique-n4-loading wf-universlt..."
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 6,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 5,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 6,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 5,
+ "nodeId": 3
+}
+[1642768236.213][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 6,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 7,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading wf-universltw0147lightcn-n4-loading wf-universltw0155oblique-n4-loading wf-universlt..."
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 8,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 7,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 8,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 7,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 8,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 9,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading wf-universltw0147lightcn-n4-loading wf-universltw0155oblique-n4-loading wf-universlt..."
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 10,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 9,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 10,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 9,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 10,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 11,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading wf-universltw0147lightcn-n4-loading wf-universltw0155oblique-n4-loading wf-universlt..."
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 12,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 11,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 12,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 11,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 12,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 13,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading wf-universltw0147lightcn-n4-loading wf-universltw0155oblique-n4-loading wf-universlt..."
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 14,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 13,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 14,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 13,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 14,
+ "nodeId": 3
+}
+[1642768236.214][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 15,
+ "nodeId": 3
+}
+[1642768236.215][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=21) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768236.225][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=21) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768236.239][DEBUG]: DevTools WebSocket Event: Page.frameAttached EBDCB326EE7911F3266EA857DCFF7A23 {
+ "frameId": "D7A230BB5A0E6775E6A1F34CF20E8CA9",
+ "parentFrameId": "EBDCB326EE7911F3266EA857DCFF7A23",
+ "stack": {
+ "callFrames": [ {
+ "columnNumber": 847,
+ "functionName": "",
+ "lineNumber": 137,
+ "scriptId": "23",
+ "url": "https://www.goldmansachs.com/"
+ }, {
+ "columnNumber": 1269,
+ "functionName": "",
+ "lineNumber": 137,
+ "scriptId": "23",
+ "url": "https://www.goldmansachs.com/"
+ }, {
+ "columnNumber": 3041,
+ "functionName": "",
+ "lineNumber": 137,
+ "scriptId": "23",
+ "url": "https://www.goldmansachs.com/"
+ } ]
+ }
+}
+[1642768236.239][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=22) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768236.239][DEBUG]: DevTools WebSocket Event: Page.frameStartedLoading EBDCB326EE7911F3266EA857DCFF7A23 {
+ "frameId": "D7A230BB5A0E6775E6A1F34CF20E8CA9"
+}
+[1642768236.250][DEBUG]: DevTools WebSocket Event: Runtime.executionContextCreated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "context": {
+ "auxData": {
+ "frameId": "D7A230BB5A0E6775E6A1F34CF20E8CA9",
+ "isDefault": true,
+ "type": "default"
+ },
+ "id": 2,
+ "name": "",
+ "origin": "https://www.goldmansachs.com",
+ "uniqueId": "-9092347270014312505.941165414401362757"
+ }
+}
+[1642768236.251][DEBUG]: DevTools WebSocket Event: Page.documentOpened EBDCB326EE7911F3266EA857DCFF7A23 {
+ "frame": {
+ "adFrameStatus": {
+ "adFrameType": "none",
+ "explanations": [ ]
+ },
+ "crossOriginIsolatedContextType": "NotIsolated",
+ "domainAndRegistry": "goldmansachs.com",
+ "gatedAPIFeatures": [ ],
+ "id": "D7A230BB5A0E6775E6A1F34CF20E8CA9",
+ "loaderId": "C953959AF84B36D9830A5DA78BC9641C",
+ "mimeType": "text/html",
+ "name": "",
+ "parentId": "EBDCB326EE7911F3266EA857DCFF7A23",
+ "secureContextType": "Secure",
+ "securityOrigin": "https://www.goldmansachs.com",
+ "url": "https://www.goldmansachs.com/"
+ }
+}
+[1642768236.251][DEBUG]: DevTools WebSocket Event: Page.frameStoppedLoading EBDCB326EE7911F3266EA857DCFF7A23 {
+ "frameId": "D7A230BB5A0E6775E6A1F34CF20E8CA9"
+}
+[1642768236.333][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 16,
+ "nodeId": 3
+}
+[1642768236.434][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=22) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768236.461][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 15,
+ "nodeId": 3
+}
+[1642768236.461][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 14,
+ "nodeId": 3
+}
+[1642768236.461][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading wf-universltw0155oblique-n4-loading wf-universltw0155roman-n4-loading wf-universltw0..."
+}
+[1642768236.461][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=23) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768236.462][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 13,
+ "nodeId": 3
+}
+[1642768236.462][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 12,
+ "nodeId": 3
+}
+[1642768236.462][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading wf-universltw0155oblique-n4-loading wf-universltw0157condense723821-n4-loading wf-un..."
+}
+[1642768236.462][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 11,
+ "nodeId": 3
+}
+[1642768236.462][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 10,
+ "nodeId": 3
+}
+[1642768236.462][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading wf-universltw0155oblique-n4-loading wf-universltw0159ultracn-n4-loading wf-universlt..."
+}
+[1642768236.463][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 9,
+ "nodeId": 3
+}
+[1642768236.463][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 8,
+ "nodeId": 3
+}
+[1642768236.463][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading wf-universltw0155oblique-n4-loading wf-universltw0165bold-n4-loading wf-universltw01..."
+}
+[1642768236.463][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 7,
+ "nodeId": 3
+}
+[1642768236.463][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 6,
+ "nodeId": 3
+}
+[1642768236.463][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading wf-universltw0155oblique-n4-loading wf-universltw0167boldcn-n4-loading"
+}
+[1642768236.464][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 5,
+ "nodeId": 3
+}
+[1642768236.464][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 4,
+ "nodeId": 3
+}
+[1642768236.464][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading wf-universltw0155oblique-n4-loading"
+}
+[1642768236.483][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=23) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768236.484][DEBUG]: DevTools WebSocket Event: Log.entryAdded EBDCB326EE7911F3266EA857DCFF7A23 {
+ "entry": {
+ "level": "error",
+ "networkRequestId": "26420.195",
+ "source": "network",
+ "text": "Failed to load resource: net::ERR_NAME_NOT_RESOLVED",
+ "timestamp": 1.642768236484736e+12,
+ "url": "http://home.web.gs.com/inettag.js"
+ }
+}
+[1642768236.484][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=24) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768236.495][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=24) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768236.585][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 3,
+ "nodeId": 3
+}
+[1642768236.585][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 2,
+ "nodeId": 3
+}
+[1642768236.585][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder wf-loading"
+}
+[1642768236.585][DEBUG]: DevTools WebSocket Event: DOM.attributeModified EBDCB326EE7911F3266EA857DCFF7A23 {
+ "name": "class",
+ "nodeId": 3,
+ "value": "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder"
+}
+[1642768236.585][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=25) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768236.645][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=25) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768236.957][DEBUG]: DevTools WebSocket Event: DOM.documentUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768236.957][DEBUG]: DevTools WebSocket Command: DOM.getDocument (id=26) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768236.957][DEBUG]: DevTools WebSocket Event: Page.domContentEventFired EBDCB326EE7911F3266EA857DCFF7A23 {
+ "timestamp": 124223.065315
+}
+[1642768236.957][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=27) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768236.963][DEBUG]: DevTools WebSocket Response: DOM.getDocument (id=26) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "root": {
+ "backendNodeId": 1,
+ "baseURL": "https://www.goldmansachs.com/",
+ "childNodeCount": 2,
+ "children": [ {
+ "backendNodeId": 2,
+ "localName": "",
+ "nodeId": 5,
+ "nodeName": "html",
+ "nodeType": 10,
+ "nodeValue": "",
+ "parentId": 4,
+ "publicId": "",
+ "systemId": ""
+ }, {
+ "attributes": [ "lang", "en-US", "itemscope", "", "itemtype", "http://schema.org/Article", "style", "", "class", "js hashchange history rgba hsla borderimage borderradius boxshadow opacity fontface video placeholder" ],
+ "backendNodeId": 3,
+ "childNodeCount": 2,
+ "children": [ {
+ "attributes": [ ],
+ "backendNodeId": 401,
+ "childNodeCount": 83,
+ "localName": "head",
+ "nodeId": 7,
+ "nodeName": "HEAD",
+ "nodeType": 1,
+ "nodeValue": "",
+ "parentId": 6
+ }, {
+ "attributes": [ "id", "homepage", "class", "interim-footer gs-v2 top-level full-width supress-series gs-sans max-width-1920 no-touch vt-body" ],
+ "backendNodeId": 21,
+ "childNodeCount": 10,
+ "localName": "body",
+ "nodeId": 8,
+ "nodeName": "BODY",
+ "nodeType": 1,
+ "nodeValue": "",
+ "parentId": 6
+ } ],
+ "frameId": "EBDCB326EE7911F3266EA857DCFF7A23",
+ "localName": "html",
+ "nodeId": 6,
+ "nodeName": "HTML",
+ "nodeType": 1,
+ "nodeValue": "",
+ "parentId": 4
+ } ],
+ "compatibilityMode": "NoQuirksMode",
+ "documentURL": "https://www.goldmansachs.com/",
+ "localName": "",
+ "nodeId": 4,
+ "nodeName": "#document",
+ "nodeType": 9,
+ "nodeValue": "",
+ "xmlVersion": ""
+ }
+}
+[1642768236.963][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=27) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768237.468][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=28) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768237.504][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=28) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768237.810][DEBUG]: DevTools WebSocket Event: DOM.inlineStyleInvalidated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "nodeIds": [ 6 ]
+}
+[1642768237.810][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=29) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768237.811][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=29) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768238.319][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=30) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768238.319][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=30) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768238.443][DEBUG]: DevTools WebSocket Event: DOM.childNodeCountUpdated EBDCB326EE7911F3266EA857DCFF7A23 {
+ "childNodeCount": 11,
+ "nodeId": 8
+}
+[1642768238.443][DEBUG]: DevTools WebSocket Event: Page.loadEventFired EBDCB326EE7911F3266EA857DCFF7A23 {
+ "timestamp": 124224.509529
+}
+[1642768238.443][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=31) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "awaitPromise": false,
+ "expression": "document.readyState",
+ "returnByValue": true
+}
+[1642768238.443][DEBUG]: DevTools WebSocket Event: Page.frameStoppedLoading EBDCB326EE7911F3266EA857DCFF7A23 {
+ "frameId": "EBDCB326EE7911F3266EA857DCFF7A23"
+}
+[1642768238.453][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=31) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "type": "string",
+ "value": "complete"
+ }
+}
+[1642768238.453][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=32) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768238.467][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=32) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768238.467][INFO]: Done waiting for pending navigations. Status: ok
+[1642768238.467][INFO]: [7877b0b61dac0daae69a230280b72e8a] RESPONSE Navigate
+[1642768238.470][INFO]: [7877b0b61dac0daae69a230280b72e8a] COMMAND Screenshot {
+
+}
+[1642768238.470][INFO]: Waiting for pending navigations...
+[1642768238.470][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=33) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768238.470][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=33) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768238.470][INFO]: Done waiting for pending navigations. Status: ok
+[1642768238.470][DEBUG]: DevTools HTTP Request: http://localhost:63991/json/activate/EBDCB326EE7911F3266EA857DCFF7A23
+[1642768238.471][DEBUG]: DevTools HTTP Response: Target activated
+[1642768238.471][DEBUG]: DevTools WebSocket Command: Page.captureScreenshot (id=34) EBDCB326EE7911F3266EA857DCFF7A23 {
+
+}
+[1642768239.234][DEBUG]: DevTools WebSocket Response: Page.captureScreenshot (id=34) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "data": "iVBORw0KGgoAAAANSUhEUgAABQ8AAAN4CAYAAABtTh7sAAAAAXNSR0IArs4c6QAAIABJREFUeJzs3X2wXGdh5/nv83LO6e77IuvKspFsg4yM7GDJNn6BQsTEEMYzUGOMh8zYs2zxMjPLhqpAQhKobCZkdyczO6mQCTVhZ0yxNQOkll2TCcEvFCyuBAxe24stO4Blg..."
+}
+[1642768239.238][INFO]: Waiting for pending navigations...
+[1642768239.238][DEBUG]: DevTools WebSocket Command: Runtime.evaluate (id=35) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "expression": "1"
+}
+[1642768239.240][DEBUG]: DevTools WebSocket Response: Runtime.evaluate (id=35) EBDCB326EE7911F3266EA857DCFF7A23 {
+ "result": {
+ "description": "1",
+ "type": "number",
+ "value": 1
+ }
+}
+[1642768239.240][INFO]: Done waiting for pending navigations. Status: ok
+[1642768239.242][INFO]: [7877b0b61dac0daae69a230280b72e8a] RESPONSE Screenshot "iVBORw0KGgoAAAANSUhEUgAABQ8AAAN4CAYAAABtTh7sAAAAAXNSR0IArs4c6QAAIABJREFUeJzs3X2wXGdh5/nv83LO6e77IuvKspFsg4yM7GDJNn6BQsTEEMYzUGOMh8zYs2zxMjPLhqpAQhKobCZkdyczO6mQCTVhZ0yxNQOkll2TCcEvFCyuBAxe24stO4Blg..."
+[1642768239.596][INFO]: [7877b0b61dac0daae69a230280b72e8a] COMMAND Quit {
+
+}
+[1642768239.637][INFO]: [7877b0b61dac0daae69a230280b72e8a] RESPONSE Quit
+[1642768239.637][DEBUG]: Log type 'driver' lost 0 entries on destruction
+[1642768239.637][DEBUG]: Log type 'browser' lost 2 entries on destruction
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/conda.yaml b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/conda.yaml
new file mode 100644
index 00000000..8de76047
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/conda.yaml
@@ -0,0 +1,16 @@
+channels:
+ # Define conda channels here.
+ - conda-forge
+
+dependencies:
+ # Define conda packages here.
+ # If available, always prefer the conda version of a package, installation will be faster and more efficient.
+ # https://anaconda.org/search
+ - python=3.7.5
+ - FPDF
+
+ - pip=20.1
+ - pip:
+ # Define pip packages here.
+ # https://pypi.org/
+ - rpaframework==11.4.0 # https://rpaframework.org/releasenotes.html
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/devdata/.ipynb_checkpoints/env-checkpoint.json b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/devdata/.ipynb_checkpoints/env-checkpoint.json
new file mode 100644
index 00000000..5d274c6b
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/devdata/.ipynb_checkpoints/env-checkpoint.json
@@ -0,0 +1,4 @@
+{
+ "RPA_SECRET_MANAGER": "RPA.Robocorp.Vault.FileSecrets",
+ "RPA_SECRET_FILE": "C:\\Users\\DELL\\vault.json"
+}
\ No newline at end of file
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/devdata/env.json b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/devdata/env.json
new file mode 100644
index 00000000..d53714e8
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/devdata/env.json
@@ -0,0 +1,4 @@
+{
+ "RPA_SECRET_MANAGER": "RPA.Robocorp.Vault.FileSecrets",
+ "RPA_SECRET_FILE": "vault.json"
+}
\ No newline at end of file
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/geckodriver-1.log b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/geckodriver-1.log
new file mode 100644
index 00000000..e69de29b
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/pdfGen.py b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/pdfGen.py
new file mode 100644
index 00000000..35c7fe92
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/pdfGen.py
@@ -0,0 +1,86 @@
+# +
+import os
+from fpdf import FPDF
+from PIL import Image
+
+pdf = FPDF(orientation = 'P', unit = 'mm', format='A4')
+
+def new_pdf():
+ pdf.add_page()
+
+ # Set Margin
+ pdf.l_margin = pdf.l_margin*1.0
+ pdf.r_margin = pdf.r_margin*1.0
+ pdf.t_margin = pdf.t_margin*1.0
+ pdf.b_margin = pdf.b_margin*1.0
+ # Effective page width and height
+ epw = pdf.w - pdf.l_margin - pdf.r_margin
+ eph = pdf.h - pdf.t_margin - pdf.b_margin
+ # Draw new margins.
+ pdf.rect(pdf.l_margin, pdf.t_margin, w=epw, h=eph)
+
+ # set style and size of font
+ # that you want in the pdf
+ pdf.set_font("Arial", 'BU', 18.0)
+ pdf.ln(10)
+ # create a cell
+ pdf.cell(200, 10, txt = "New Business License Application",
+ ln = 2, align = 'C')
+ pdf.ln(2)
+ # add another cell
+ pdf.cell(200, 10, txt = "Background Verification",
+ ln = 2, align = 'C')
+ pdf.ln(10)
+
+def add_new_heading(headerText):
+ # set style and size of font
+ pdf.set_font("Arial", 'U', size = 12)
+ pdf.set_text_color(0, 0, 255)
+ # add a cell
+ pdf.cell(200, 0, txt = headerText,
+ ln = 2, align = 'C')
+
+def add_new_page(headerText):
+ pdf.add_page()
+
+ # Set Margin
+ pdf.l_margin = pdf.l_margin*1.0
+ pdf.r_margin = pdf.r_margin*1.0
+ pdf.t_margin = pdf.t_margin*1.0
+ pdf.b_margin = pdf.b_margin*1.0
+ # Effective page width and height
+ epw = pdf.w - pdf.l_margin - pdf.r_margin
+ eph = pdf.h - pdf.t_margin - pdf.b_margin
+ # Draw new margins.
+ pdf.rect(pdf.l_margin, pdf.t_margin, w=epw, h=eph)
+ pdf.ln(20)
+ # set style and size of font
+ pdf.set_font("Arial", 'U', size = 12)
+ pdf.set_text_color(0, 0, 255)
+ # add a cell
+ pdf.cell(200, 10, txt = headerText,
+ ln = 2, align = 'C')
+
+def add_image_file(image):
+ pdf.image(image, 30, 80, 135)
+
+def footer(self):
+ # Go to 1.5 cm from bottom
+ self.set_y(-15)
+ # Select Arial italic 8
+ self.set_font('Arial', 'I', 8)
+ # Print centered page number
+ self.cell(0, 10, 'Page %s' % self.page_no(), 0, 0, 'C')
+
+def header():
+ # Arial bold 15
+ pdf.set_font('Arial','B',15);
+ # Move to the right
+ pdf.cell(80);
+ # Title
+ pdf.cell(30,10,'AOT TECHNOLOGIES',1,0,'C');
+ # Line break
+ pdf.ln(20);
+
+def save_pdf_file(pdfFile):
+ pdf.output(pdfFile)
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/robot.yaml b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/robot.yaml
new file mode 100644
index 00000000..c141220a
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/robot.yaml
@@ -0,0 +1,12 @@
+tasks:
+ Default:
+ shell: python -m robot --report NONE --outputdir output --logtitle "Task log"
+
+condaConfigFile: conda.yaml
+artifactsDir: output
+PATH:
+ - .
+PYTHONPATH:
+ - .
+ignoreFiles:
+ - .gitignore
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/tasks.robot b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/tasks.robot
new file mode 100644
index 00000000..1a35fcc1
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/tasks.robot
@@ -0,0 +1,135 @@
+# +
+*** Settings ***
+Documentation Background check for New Business License Application
+... Capture screenshots of the below categories and append it to a PDF file.
+... 1. Social media company profile like Linkedin, Twitter, Facebook and Instagram.
+... 2. Checking if Business have a working website.
+... 3. Google search results about company.
+... 4. Validating if company is blacklisted or not. (Assuming the business is going to start in Canada)
+
+
+#Imported Libraries
+
+Library RPA.Robocloud.Secrets
+Library RPA.Browser.Selenium
+Library RPA.FileSystem
+Library Collections
+Library RPA.Archive
+Library pdfGen.py
+Library RPA.HTTP
+Library RPA.PDF
+Library DateTime
+# -
+
+
+*** Variables ***
+${GLOBAL_RETRY_AMOUNT}= 3x
+${GLOBAL_RETRY_INTERVAL}= 1s
+${MAX_SEARCH_RESULT_CLICKABLE}= 3
+${RUNTIME_DIR}= ${CURDIR}${/}output${/}${working-dir}${/}runtime
+${FINAL_DIR}= ${CURDIR}${/}output${/}${working-dir}${/}final
+${search-engine-url}= https://google.com
+${verify-bc-org-url}= https://www.orgbook.gov.bc.ca/en/home
+
+*** Keywords ***
+
+Get Data From Vault
+ Log To Console START : Getting data from the vault
+ ${secret}= Get Secret my_vault
+ Log To Console START : Getting data from the vault
+ [Return] ${secret}
+
+Gather The Social Media Profiles
+
+ New Pdf
+
+ Add New Heading Validate If Company Was Blacklisted
+
+ Validate If Company Was Blacklisted ${businessOperatingName}
+
+ Add New Page Search In Intranet About The Company
+
+ Search In Intranet About The Company ${businessOperatingName}
+
+ ${profiles} = Create List facebook linkedin twitter instagram
+
+ Search And Append Social Media Search Results ${businessOperatingName} ${profiles}
+
+ Add New Page Validate Company Website
+
+ Open And Validate Company Website ${businessWebsite}
+
+ Log To Console Checks completed
+
+ Save Pdf File ${FINAL_DIR}${/}${working-dir}.pdf
+
+
+Search And Append Social Media Search Results
+ [Arguments] ${businessOperatingName} ${profiles}
+ Log To Console Started : Searching in facebook for company profiles
+ Open Available Browser ${search-engine-url}
+ FOR ${profile} IN @{profiles}
+ Input Text //input[@title="Search"] ${businessOperatingName} ${profile}
+ Submit Form
+ Wait Until Element Is Visible //div[@id="center_col"]
+ Take a screenshot of the page //div[@id="center_col"] ${profile}
+ Add New Page ${profile}
+ Add Image File ${RUNTIME_DIR}${/}${profile}.png
+ Go Back
+ END
+ Log To Console Completed : Searching in facebook for company profiles
+ [Teardown] Close Intranet Browser
+
+Search In Intranet About The Company
+ [Arguments] ${businessOperatingName}
+ Log To Console Started : Searching in intranet about the company
+ Open Available Browser ${search-engine-url}
+ Input Text //input[@title="Search"] ${businessOperatingName}
+ Submit Form
+ Wait Until Element Is Visible //div[@id="rcnt"]
+ Take a screenshot of the page //div[@id="rcnt"] searchResults
+ Add Image File ${RUNTIME_DIR}${/}searchResults.png
+ Log To Console Completed : Searching in intranet about the company
+ [Teardown] Close Intranet Browser
+
+Open And Validate Company Website
+ [Arguments] ${businessWebsite}
+ Log To Console Started : Open And Validate Company Website
+ ${length}= Get Length ${businessWebsite}
+ ${fileName}= Set Variable ${RUNTIME_DIR}${/}companyWebsite.png
+ IF ${length} > 0
+ Open Available Browser ${businessWebsite}
+ Capture Page Screenshot ${fileName}
+ Add Image File ${fileName}
+ END
+ Log To Console End : Open And Validate Company Website
+ [Teardown] Close Intranet Browser
+
+Validate If Company Was Blacklisted
+ [Arguments] ${businessOperatingName}
+ Log To Console Started : Validate If Company Was Blacklisted
+ Open Available Browser ${verify-bc-org-url}
+ Wait Until Element Is Visible //input[@id="searchBar"]
+ Input Text //input[@id="searchBar"] ${businessOperatingName}
+ Click Button //button[@id="searchBarButton"]
+ Sleep 5s
+ Take a screenshot of the page //div[@class="v-main__wrap"] blacklistdata
+ Add Image File ${RUNTIME_DIR}${/}blacklistdata.png
+ Log To Console End : Validate If Company Was Blacklisted
+ [Teardown] Close Intranet Browser
+
+Take a screenshot of the page
+ [Arguments] ${webEl} ${name}
+ Sleep 1s
+ Wait Until Page Contains Element ${webEl} timeout=20
+ Screenshot ${webEl} ${RUNTIME_DIR}${/}${name}.png
+
+
+
+Close Intranet Browser
+ Close Browser
+
+*** Tasks ***
+Do Background check for the New Business Application
+ Gather The Social Media Profiles
+ [Teardown] Close Intranet Browser
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/vault.json b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/vault.json
new file mode 100644
index 00000000..c28f72ad
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/background-check/vault.json
@@ -0,0 +1,6 @@
+{
+ "my_vault": {
+ "search-engine-url" : "https://google.com",
+ "verify-bc-org-url" : "https://www.orgbook.gov.bc.ca/en/home"
+ }
+}
\ No newline at end of file
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/Keywords.Library/Browser.Library/.ipynb_checkpoints/browser-checkpoint.robot b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/Keywords.Library/Browser.Library/.ipynb_checkpoints/browser-checkpoint.robot
new file mode 100644
index 00000000..d18f6e74
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/Keywords.Library/Browser.Library/.ipynb_checkpoints/browser-checkpoint.robot
@@ -0,0 +1,39 @@
+*** Settings ***
+Documentation Browser Keywords for web scrapper
+Library RPA.Browser.Selenium
+Library RPA.Tables
+Library RPA.Excel.Files
+Library String
+
+*** Variables ***
+${inputEl} //input[@id="twotabsearchtextbox"]
+${dataEl} //div[@class="s-result-item s-asin sg-col-0-of-12 sg-col-16-of-20 sg-col sg-col-12-of-16"]
+${itemNameEl} xpath://span[@class="a-size-medium a-color-base a-text-normal"]
+${itemPriceEl} xpath://span[@class="a-price-whole"]
+
+*** Keywords ***
+Open Browser And Do Web Scrapping And Generate Excel
+ [Arguments] ${URL} ${SEARCH_KEY} ${OUTPUT_FILE}
+ Open Available Browser ${URL}
+ Input Text When Element Is Visible ${inputEl} ${SEARCH_KEY}
+ Sleep 1
+ Submit Form
+ Sleep 1
+ ${itemNames} = Get WebElements ${itemNameEl}
+ ${itemPrices} = Get WebElements ${itemPriceEl}
+ Create Workbook ${OUTPUT_FILE}
+ FOR ${itemName} ${itemPrice} IN ZIP ${itemNames} ${itemPrices}
+ ${productName} = Get Text ${itemName}
+ ${productPrice} = Get Text ${itemPrice}
+ ${productPrice} = Remove String Using Regexp
+ ... ${productPrice} ,
+ ${productPrice} = Convert To Number ${productPrice}
+ &{row} = Create Dictionary
+ ... Product Name ${productName}
+ ... Price (In INR) ${productPrice}
+ ... Price (In USD) ${productPrice / 74.13}
+ Append Rows To Worksheet ${row} header=${TRUE}
+ END
+ Save Workbook
+ Close Workbook
+ Close Browser
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/Keywords.Library/Browser.Library/browser.robot b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/Keywords.Library/Browser.Library/browser.robot
new file mode 100644
index 00000000..d18f6e74
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/Keywords.Library/Browser.Library/browser.robot
@@ -0,0 +1,39 @@
+*** Settings ***
+Documentation Browser Keywords for web scrapper
+Library RPA.Browser.Selenium
+Library RPA.Tables
+Library RPA.Excel.Files
+Library String
+
+*** Variables ***
+${inputEl} //input[@id="twotabsearchtextbox"]
+${dataEl} //div[@class="s-result-item s-asin sg-col-0-of-12 sg-col-16-of-20 sg-col sg-col-12-of-16"]
+${itemNameEl} xpath://span[@class="a-size-medium a-color-base a-text-normal"]
+${itemPriceEl} xpath://span[@class="a-price-whole"]
+
+*** Keywords ***
+Open Browser And Do Web Scrapping And Generate Excel
+ [Arguments] ${URL} ${SEARCH_KEY} ${OUTPUT_FILE}
+ Open Available Browser ${URL}
+ Input Text When Element Is Visible ${inputEl} ${SEARCH_KEY}
+ Sleep 1
+ Submit Form
+ Sleep 1
+ ${itemNames} = Get WebElements ${itemNameEl}
+ ${itemPrices} = Get WebElements ${itemPriceEl}
+ Create Workbook ${OUTPUT_FILE}
+ FOR ${itemName} ${itemPrice} IN ZIP ${itemNames} ${itemPrices}
+ ${productName} = Get Text ${itemName}
+ ${productPrice} = Get Text ${itemPrice}
+ ${productPrice} = Remove String Using Regexp
+ ... ${productPrice} ,
+ ${productPrice} = Convert To Number ${productPrice}
+ &{row} = Create Dictionary
+ ... Product Name ${productName}
+ ... Price (In INR) ${productPrice}
+ ... Price (In USD) ${productPrice / 74.13}
+ Append Rows To Worksheet ${row} header=${TRUE}
+ END
+ Save Workbook
+ Close Workbook
+ Close Browser
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/Keywords.Library/Excel.Library/.ipynb_checkpoints/excel-checkpoint.robot b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/Keywords.Library/Excel.Library/.ipynb_checkpoints/excel-checkpoint.robot
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/Keywords.Library/Excel.Library/.ipynb_checkpoints/excel-checkpoint.robot
@@ -0,0 +1 @@
+
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/Keywords.Library/Excel.Library/excel.robot b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/Keywords.Library/Excel.Library/excel.robot
new file mode 100644
index 00000000..ac8ff6d1
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/Keywords.Library/Excel.Library/excel.robot
@@ -0,0 +1,6 @@
+*** Settings ***
+Library RPA.Excel.Files
+
+*** Keywords ***
+entering value in bbb
+ Set Cell Value 10 1 23 data
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/conda.yaml b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/conda.yaml
new file mode 100644
index 00000000..a2e9e121
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/conda.yaml
@@ -0,0 +1,15 @@
+channels:
+ # Define conda channels here.
+ - conda-forge
+
+dependencies:
+ # Define conda packages here.
+ # If available, always prefer the conda version of a package, installation will be faster and more efficient.
+ # https://anaconda.org/search
+ - python=3.7.5
+
+ - pip=20.1
+ - pip:
+ # Define pip packages here.
+ # https://pypi.org/
+ - rpaframework==10.3.0 # https://rpaframework.org/releasenotes.html
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/devdata/env.json b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/devdata/env.json
new file mode 100644
index 00000000..d53714e8
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/devdata/env.json
@@ -0,0 +1,4 @@
+{
+ "RPA_SECRET_MANAGER": "RPA.Robocorp.Vault.FileSecrets",
+ "RPA_SECRET_FILE": "vault.json"
+}
\ No newline at end of file
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/pdfGen.py b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/pdfGen.py
new file mode 100644
index 00000000..723f3526
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/pdfGen.py
@@ -0,0 +1,86 @@
+# +
+import os
+from fpdf import FPDF
+from PIL import Image
+
+pdf = FPDF(orientation = 'P', unit = 'mm', format='A4')
+
+def new_pdf():
+ pdf.add_page()
+
+ # Set Margin
+ pdf.l_margin = pdf.l_margin*1.0
+ pdf.r_margin = pdf.r_margin*1.0
+ pdf.t_margin = pdf.t_margin*1.0
+ pdf.b_margin = pdf.b_margin*1.0
+ # Effective page width and height
+ epw = pdf.w - pdf.l_margin - pdf.r_margin
+ eph = pdf.h - pdf.t_margin - pdf.b_margin
+ # Draw new margins.
+ pdf.rect(pdf.l_margin, pdf.t_margin, w=epw, h=eph)
+
+ # set style and size of font
+ # that you want in the pdf
+ pdf.set_font("Arial", 'BU', 18.0)
+ pdf.ln(10)
+ # create a cell
+ pdf.cell(200, 10, txt = "Freedom Of Information and Protection of Privacy",
+ ln = 2, align = 'C')
+ pdf.ln(2)
+ # add another cell
+ pdf.cell(200, 10, txt = "Background Verification",
+ ln = 2, align = 'C')
+ pdf.ln(10)
+
+def add_new_heading(headerText):
+ # set style and size of font
+ pdf.set_font("Arial", 'U', size = 12)
+ pdf.set_text_color(0, 0, 255)
+ # add a cell
+ pdf.cell(200, 0, txt = headerText,
+ ln = 2, align = 'C')
+
+def add_new_page(headerText):
+ pdf.add_page()
+
+ # Set Margin
+ pdf.l_margin = pdf.l_margin*1.0
+ pdf.r_margin = pdf.r_margin*1.0
+ pdf.t_margin = pdf.t_margin*1.0
+ pdf.b_margin = pdf.b_margin*1.0
+ # Effective page width and height
+ epw = pdf.w - pdf.l_margin - pdf.r_margin
+ eph = pdf.h - pdf.t_margin - pdf.b_margin
+ # Draw new margins.
+ pdf.rect(pdf.l_margin, pdf.t_margin, w=epw, h=eph)
+ pdf.ln(20)
+ # set style and size of font
+ pdf.set_font("Arial", 'U', size = 12)
+ pdf.set_text_color(0, 0, 255)
+ # add a cell
+ pdf.cell(200, 10, txt = headerText,
+ ln = 2, align = 'C')
+
+def add_image_file(image):
+ pdf.image(image, 30, 80, 135)
+
+def footer(self):
+ # Go to 1.5 cm from bottom
+ self.set_y(-15)
+ # Select Arial italic 8
+ self.set_font('Arial', 'I', 8)
+ # Print centered page number
+ self.cell(0, 10, 'Page %s' % self.page_no(), 0, 0, 'C')
+
+def header():
+ # Arial bold 15
+ pdf.set_font('Arial','B',15);
+ # Move to the right
+ pdf.cell(80);
+ # Title
+ pdf.cell(30,10,'AOT TECHNOLOGIES',1,0,'C');
+ # Line break
+ pdf.ln(20);
+
+def save_pdf_file(pdfFile):
+ pdf.output(pdfFile)
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/robot.yaml b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/robot.yaml
new file mode 100644
index 00000000..c141220a
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/robot.yaml
@@ -0,0 +1,12 @@
+tasks:
+ Default:
+ shell: python -m robot --report NONE --outputdir output --logtitle "Task log"
+
+condaConfigFile: conda.yaml
+artifactsDir: output
+PATH:
+ - .
+PYTHONPATH:
+ - .
+ignoreFiles:
+ - .gitignore
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/tasks.robot b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/tasks.robot
new file mode 100644
index 00000000..48c26b1b
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/tasks.robot
@@ -0,0 +1,67 @@
+*** Settings ***
+Documentation Web Scrapper with excel output
+Resource ./Keywords.Library/Browser.Library/browser.robot
+
+#Imported Libraries
+
+Library RPA.Robocloud.Secrets
+Library RPA.Browser.Selenium
+Library RPA.FileSystem
+Library Collections
+Library RPA.Archive
+Library pdfGen.py
+Library RPA.HTTP
+Library RPA.PDF
+Library DateTime
+# -
+
+
+*** Variables ***
+${GLOBAL_RETRY_AMOUNT}= 3x
+${GLOBAL_RETRY_INTERVAL}= 1s
+${MAX_SEARCH_RESULT_CLICKABLE}= 3
+${RUNTIME_DIR}= ${CURDIR}${/}output${/}${working-dir}${/}runtime
+${FINAL_DIR}= ${CURDIR}${/}output${/}${working-dir}${/}final
+${search-engine-url}= https://google.com
+
+*** Keywords ***
+
+Gather Organization Details
+
+ New Pdf
+
+ Search Organization Details ${organizationName}
+
+ Log To Console Checks completed
+
+ Save Pdf File ${FINAL_DIR}${/}${working-dir}.pdf
+
+Search Organization Details
+ [Arguments] ${organizationName}
+ Log To Console Started : Searching in intranet about the company
+ Open Available Browser ${search-engine-url}
+ Input Text //input[@title="Search"] ${organizationName}
+ Submit Form
+ Wait Until Element Is Visible //div[@id="rcnt"]
+ Take a screenshot of the page //div[@id="rcnt"] searchResults
+ Add Image File ${RUNTIME_DIR}${/}searchResults.png
+ Log To Console Completed : Searching in intranet about the company
+ [Teardown] Close Intranet Browser
+
+
+
+Take a screenshot of the page
+ [Arguments] ${webEl} ${name}
+ Sleep 1s
+ Wait Until Page Contains Element ${webEl} timeout=20
+ Screenshot ${webEl} ${RUNTIME_DIR}${/}${name}.png
+
+
+
+Close Intranet Browser
+ Close Browser
+
+*** Tasks ***
+Do Background check for the Freedom of Information Application
+ Gather Organization Details
+ [Teardown] Close Intranet Browser
diff --git a/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/vault.json b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/vault.json
new file mode 100644
index 00000000..c28f72ad
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/src/main/resources/robots/web-scraper/vault.json
@@ -0,0 +1,6 @@
+{
+ "my_vault": {
+ "search-engine-url" : "https://google.com",
+ "verify-bc-org-url" : "https://www.orgbook.gov.bc.ca/en/home"
+ }
+}
\ No newline at end of file
diff --git a/rpa-robocorp-extention/external-client-extention/starter-examples/handlers/background-check-robot-handler-readme.md b/rpa-robocorp-extention/external-client-extention/starter-examples/handlers/background-check-robot-handler-readme.md
new file mode 100644
index 00000000..4b70ce6f
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/starter-examples/handlers/background-check-robot-handler-readme.md
@@ -0,0 +1,48 @@
+# Background Check Robot Handler
+
+**org.camunda.rpa.client.handlers.impl.BackgroundCheckRobotHandler**
+
+ This component can be used to handle and manage the robot.
+
+## Table of Content
+* [Type](#type)
+* [How it Works](#how-it-works)
+* [How to Configure](#how-to-configure)
+
+### Type
+
+Java Handler
+
+### How it Works
+
+This component will manage the robot to build a script with input parameters and run the robot.
+
+### How to Configure
+
+Below instructions can be followed to configure a robot handler to manage your robot.
+
+* Subscribe the Handler to a topic with `@ExternalTaskSubscription` annotation and `topic name`. `eg:@ExternalTaskSubscription("background-check")`
+
+* The robot Handler implements `IRobotHandler` interface and `ExternalTaskHandler` interface.
+
+* Override the below methods and provide business logics. These methods are used to manage and run the robot successfully.
+
+ * **`startAudit`** - Create a new entry in the Audit table to acknowledge that the robot started successfully. This method invokes `org.camunda.rpa.client.core.RobotDataManager#initAudit()` to update the robot current status `eg:IN_PROGRESS`
+
+ * **`buildInputWithAdditionalVariables`** - This method will build input with additional variables for robot by invoking the `org.camunda.rpa.client.core.RobotIOManager#buildInput()`.
+
+ * **`buildInput`** - This is an overriden method expecting the parameter `ExternalTask` and `RobotHandlerConfig` to build the Input.
+
+ * **`runRobot`** - This method invokes `org.camunda.rpa.client.core.RobotDataManager#runRobot()` to find out the robot type, build a script with input parameters and run using script manager.
+
+ * **`collectResponse`** - Once robot completed its task successfully, this method invokes `org.camunda.rpa.client.core.RobotIOManager#getOutputData` to collect the response.
+
+ * **`completeAudit`** - Complete the audit process by invoking `org.camunda.rpa.client.core.RobotDataManager#finalizeAudit` and update the status in audit table. `eg:SUCCESS/FAILURE`
+
+ * **`handleFailure`** - Reports a failure to execute a task. A number of retries and a timeout untilthe task can be specified. If the retries are set to 0, an incident for thistask is created.
+
+ * **`doCleanup`** - This function will clear the robot current output directory and it's data
+
+ * **`getHandlerId`** - Each Robot handler is identified by a unique `HANDLER_ID`.
+
+* Checkout the configurations for [BackgroundCheckRobotHandler](../../src/main/java/org/camunda/rpa/client/handlers/impl/BackgroundCheckRobotHandler.java).
diff --git a/rpa-robocorp-extention/external-client-extention/starter-examples/robots/background-check-robot-readme.md b/rpa-robocorp-extention/external-client-extention/starter-examples/robots/background-check-robot-readme.md
new file mode 100644
index 00000000..713f72ea
--- /dev/null
+++ b/rpa-robocorp-extention/external-client-extention/starter-examples/robots/background-check-robot-readme.md
@@ -0,0 +1,36 @@
+# Background Check Robot
+
+ This robot will perform a background check for New Business License Application.
+
+## Table of Content
+* [Type](#type)
+* [How it Works](#how-it-works)
+* [How to Use](#how-to-use)
+
+### Type
+
+Robot
+
+### How it Works
+
+Robot will be triggered when clerk review the task. This Background Check Robot will perform below tasks:
+
+* Search in social media like Linkedin, Twitter, Facebook and Instagram for company profiles.
+* Validate the business website.
+* Fetch google search results of the company.
+* Validate if company is blacklisted or not.
+
+Finally capture the screenshot of all the task and append it to a PDF file.
+
+### How to Use
+
+Below instructions can be followed to run the robot.
+
+ * Build the robot using [Robocorp](https://robocorp.com/). Refer this [background-check](https://github.com/shibin-aot/formsflow-ai-extensions/tree/main/formsflow-robocorp-sample/camunda-springboot-external-client/external-client-extention/src/main/resources/robots/background-check) robot.
+ * Add the robot to the path `{base-dir}/src/main/resources/robots`.
+ * Make sure your current working directory is `{base-dir}/src/main/resources/robots/background-check`.
+ * To Test if robot is working, run the below command:
+
+ ```
+rcc run
+```
diff --git a/rpa-robocorp-extention/robocorp-examples/background-check-robot/.gitignore b/rpa-robocorp-extention/robocorp-examples/background-check-robot/.gitignore
new file mode 100644
index 00000000..8d658dde
--- /dev/null
+++ b/rpa-robocorp-extention/robocorp-examples/background-check-robot/.gitignore
@@ -0,0 +1 @@
+./output/*
\ No newline at end of file
diff --git a/rpa-robocorp-extention/robocorp-examples/background-check-robot/conda.yaml b/rpa-robocorp-extention/robocorp-examples/background-check-robot/conda.yaml
new file mode 100644
index 00000000..65c7d32e
--- /dev/null
+++ b/rpa-robocorp-extention/robocorp-examples/background-check-robot/conda.yaml
@@ -0,0 +1,16 @@
+channels:
+ # Define conda channels here.
+ - conda-forge
+
+dependencies:
+ # Define conda packages here.
+ # If available, always prefer the conda version of a package, installation will be faster and more efficient.
+ # https://anaconda.org/search
+ - python=3.7.5
+ - FPDF
+
+ - pip=20.1
+ - pip:
+ # Define pip packages here.
+ # https://pypi.org/
+ - rpaframework==12.6.0 # https://rpaframework.org/releasenotes.html
diff --git a/rpa-robocorp-extention/robocorp-examples/background-check-robot/mine_activate.bat b/rpa-robocorp-extention/robocorp-examples/background-check-robot/mine_activate.bat
new file mode 100644
index 00000000..12fa639b
--- /dev/null
+++ b/rpa-robocorp-extention/robocorp-examples/background-check-robot/mine_activate.bat
@@ -0,0 +1,23 @@
+SET PYTHON_EXE=C:\Users\DELL\AppData\Local\robocorp\holotree\5a1fac3c5_0bacdd96\python.exe
+SET CONDA_DEFAULT_ENV=rcc
+SET CONDA_PREFIX=C:\Users\DELL\AppData\Local\robocorp\holotree\5a1fac3c5_0bacdd96
+SET CONDA_PROMPT_MODIFIER=(rcc)
+SET CONDA_SHLVL=1
+SET PYTHONHOME=
+SET PYTHONSTARTUP=
+SET PYTHONEXECUTABLE=
+SET PYTHONNOUSERSITE=1
+SET PYTHONDONTWRITEBYTECODE=x
+SET PYTHONPYCACHEPREFIX=C:\Users\DELL\AppData\Local\robocorp\temp\810acbd444357612
+SET ROBOCORP_HOME=C:\Users\DELL\AppData\Local\robocorp
+SET RCC_ENVIRONMENT_HASH=
+SET RCC_INSTALLATION_ID=0d82799a-a5cf-12fa-782a-9e3ee2e77c02
+SET RCC_TRACKING_ALLOWED=true
+SET TEMP=C:\Users\DELL\AppData\Local\robocorp\temp\810acbd444357612
+SET TMP=C:\Users\DELL\AppData\Local\robocorp\temp\810acbd444357612
+SET PATH=D:\AOT\Project\Source\formsflow-ai-extensions\robocorp-examples\background-check-robot;C:\Users\DELL\AppData\Local\robocorp\holotree\5a1fac3c5_0bacdd96;C:\Users\DELL\AppData\Local\robocorp\holotree\5a1fac3c5_0bacdd96\Library\mingw-w64\bin;C:\Users\DELL\AppData\Local\robocorp\holotree\5a1fac3c5_0bacdd96\Library\usr\bin;C:\Users\DELL\AppData\Local\robocorp\holotree\5a1fac3c5_0bacdd96\Library\bin;C:\Users\DELL\AppData\Local\robocorp\holotree\5a1fac3c5_0bacdd96\Scripts;C:\Users\DELL\AppData\Local\robocorp\holotree\5a1fac3c5_0bacdd96\bin;C:\Program Files\Common Files\Oracle\Java\javapath;C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0;C:\Windows\System32\OpenSSH;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Program Files\Java\jdk-11.0.12\bin;C:\apache-maven-3.6.0\bin;C:\Program Files\PuTTY;C:\Program Files\Git\cmd;C:\Program Files\MySQL\MySQL Server 5.6\bin;C:\Program Files\nodejs;D:\AOT\Project\Deployment\apache-tomcat-9.0.45-windows-x64\apache-tomcat-9.0.45\bin;D:\AOT\Project\Software\grails-2.3.4\bin;C:\Program Files\TortoiseSVN\bin;C:\Users\DELL;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0;C:\WINDOWS\System32\OpenSSH;D:\AOT\Project\Software\openshift-client-windows;D:\Software\apache-ant\bin;C:\Program Files\k6;C:\Program Files\dotnet;C:\Program Files\Docker\Docker\resources\bin;C:\ProgramData\DockerDesktop\version-bin;C:\Users\DELL\AppData\Local\Microsoft\WindowsApps;C:\Users\DELL\AppData\Local\GitHubDesktop\bin;C:\Users\DELL\AppData\Roaming\npm;C:\Users\DELL\AppData\Local\Programs\Fiddler;D:\AOT\Project\Source\formsflow-ai-extensions\robocorp-examples\background-check-robot\%USERPROFILE%\AppData\Local\Microsoft\WindowsApps;D:\AOT\Project\Source\formsflow-ai-extensions\robocorp-examples\background-check-robot;C:\Program Files\liquibase
+SET PYTHONPATH=D:\AOT\Project\Source\formsflow-ai-extensions\robocorp-examples\background-check-robot
+SET ROBOT_ROOT=D:\AOT\Project\Source\formsflow-ai-extensions\robocorp-examples\background-check-robot
+SET ROBOT_ARTIFACTS=D:\AOT\Project\Source\formsflow-ai-extensions\robocorp-examples\background-check-robot\output
+SET MAMBA_ROOT_PREFIX=C:\Users\DELL\AppData\Local\robocorp
+SET PATH=C:\Users\DELL\AppData\Local\robocorp\holotree\5a1fac3c5_0bacdd96;C:\Users\DELL\AppData\Local\robocorp\holotree\5a1fac3c5_0bacdd96\Library\mingw-w64\bin;C:\Users\DELL\AppData\Local\robocorp\holotree\5a1fac3c5_0bacdd96\Library\usr\bin;C:\Users\DELL\AppData\Local\robocorp\holotree\5a1fac3c5_0bacdd96\Library\bin;C:\Users\DELL\AppData\Local\robocorp\holotree\5a1fac3c5_0bacdd96\Scripts;C:\Users\DELL\AppData\Local\robocorp\holotree\5a1fac3c5_0bacdd96\bin;C:\Program Files\Common Files\Oracle\Java\javapath;C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0;C:\Windows\System32\OpenSSH;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Program Files\Java\jdk-11.0.12\bin;C:\apache-maven-3.6.0\bin;C:\Program Files\PuTTY;C:\Program Files\Git\cmd;C:\Program Files\MySQL\MySQL Server 5.6\bin;C:\Program Files\nodejs;D:\AOT\Project\Deployment\apache-tomcat-9.0.45-windows-x64\apache-tomcat-9.0.45\bin;D:\AOT\Project\Software\grails-2.3.4\bin;C:\Program Files\TortoiseSVN\bin;C:\Users\DELL;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0;C:\WINDOWS\System32\OpenSSH;D:\AOT\Project\Software\openshift-client-windows;D:\Software\apache-ant\bin;C:\Program Files\k6;C:\Program Files\dotnet;C:\Program Files\Docker\Docker\resources\bin;C:\ProgramData\DockerDesktop\version-bin;C:\Users\DELL\AppData\Local\Microsoft\WindowsApps;C:\Users\DELL\AppData\Local\GitHubDesktop\bin;C:\Users\DELL\AppData\Roaming\npm;C:\Users\DELL\AppData\Local\Programs\Fiddler;D:\AOT\Project\Source\formsflow-ai-extensions\robocorp-examples\background-check-robot\C:\Users\DELL\AppData\Local\Microsoft\WindowsApps;D:\AOT\Project\Source\formsflow-ai-extensions\robocorp-examples\background-check-robot;C:\Program Files\liquibase
diff --git a/rpa-robocorp-extention/robocorp-examples/background-check-robot/pdfGen.py b/rpa-robocorp-extention/robocorp-examples/background-check-robot/pdfGen.py
new file mode 100644
index 00000000..35c7fe92
--- /dev/null
+++ b/rpa-robocorp-extention/robocorp-examples/background-check-robot/pdfGen.py
@@ -0,0 +1,86 @@
+# +
+import os
+from fpdf import FPDF
+from PIL import Image
+
+pdf = FPDF(orientation = 'P', unit = 'mm', format='A4')
+
+def new_pdf():
+ pdf.add_page()
+
+ # Set Margin
+ pdf.l_margin = pdf.l_margin*1.0
+ pdf.r_margin = pdf.r_margin*1.0
+ pdf.t_margin = pdf.t_margin*1.0
+ pdf.b_margin = pdf.b_margin*1.0
+ # Effective page width and height
+ epw = pdf.w - pdf.l_margin - pdf.r_margin
+ eph = pdf.h - pdf.t_margin - pdf.b_margin
+ # Draw new margins.
+ pdf.rect(pdf.l_margin, pdf.t_margin, w=epw, h=eph)
+
+ # set style and size of font
+ # that you want in the pdf
+ pdf.set_font("Arial", 'BU', 18.0)
+ pdf.ln(10)
+ # create a cell
+ pdf.cell(200, 10, txt = "New Business License Application",
+ ln = 2, align = 'C')
+ pdf.ln(2)
+ # add another cell
+ pdf.cell(200, 10, txt = "Background Verification",
+ ln = 2, align = 'C')
+ pdf.ln(10)
+
+def add_new_heading(headerText):
+ # set style and size of font
+ pdf.set_font("Arial", 'U', size = 12)
+ pdf.set_text_color(0, 0, 255)
+ # add a cell
+ pdf.cell(200, 0, txt = headerText,
+ ln = 2, align = 'C')
+
+def add_new_page(headerText):
+ pdf.add_page()
+
+ # Set Margin
+ pdf.l_margin = pdf.l_margin*1.0
+ pdf.r_margin = pdf.r_margin*1.0
+ pdf.t_margin = pdf.t_margin*1.0
+ pdf.b_margin = pdf.b_margin*1.0
+ # Effective page width and height
+ epw = pdf.w - pdf.l_margin - pdf.r_margin
+ eph = pdf.h - pdf.t_margin - pdf.b_margin
+ # Draw new margins.
+ pdf.rect(pdf.l_margin, pdf.t_margin, w=epw, h=eph)
+ pdf.ln(20)
+ # set style and size of font
+ pdf.set_font("Arial", 'U', size = 12)
+ pdf.set_text_color(0, 0, 255)
+ # add a cell
+ pdf.cell(200, 10, txt = headerText,
+ ln = 2, align = 'C')
+
+def add_image_file(image):
+ pdf.image(image, 30, 80, 135)
+
+def footer(self):
+ # Go to 1.5 cm from bottom
+ self.set_y(-15)
+ # Select Arial italic 8
+ self.set_font('Arial', 'I', 8)
+ # Print centered page number
+ self.cell(0, 10, 'Page %s' % self.page_no(), 0, 0, 'C')
+
+def header():
+ # Arial bold 15
+ pdf.set_font('Arial','B',15);
+ # Move to the right
+ pdf.cell(80);
+ # Title
+ pdf.cell(30,10,'AOT TECHNOLOGIES',1,0,'C');
+ # Line break
+ pdf.ln(20);
+
+def save_pdf_file(pdfFile):
+ pdf.output(pdfFile)
diff --git a/rpa-robocorp-extention/robocorp-examples/background-check-robot/robot.yaml b/rpa-robocorp-extention/robocorp-examples/background-check-robot/robot.yaml
new file mode 100644
index 00000000..2aa1dc23
--- /dev/null
+++ b/rpa-robocorp-extention/robocorp-examples/background-check-robot/robot.yaml
@@ -0,0 +1,12 @@
+tasks:
+ Default:
+ shell: python -m robot --report NONE --outputdir output --logtitle "Task log" tasks.robot
+
+condaConfigFile: conda.yaml
+artifactsDir: output
+PATH:
+ - .
+PYTHONPATH:
+ - .
+ignoreFiles:
+ - .gitignore
diff --git a/rpa-robocorp-extention/robocorp-examples/background-check-robot/tasks.robot b/rpa-robocorp-extention/robocorp-examples/background-check-robot/tasks.robot
new file mode 100644
index 00000000..12e40262
--- /dev/null
+++ b/rpa-robocorp-extention/robocorp-examples/background-check-robot/tasks.robot
@@ -0,0 +1,152 @@
+# +
+ # +
+*** Settings ***
+Documentation Background check for New Business License Application
+... Capture screenshots of the below categories and append it to a PDF file.
+... 1. Social media company profile like Linkedin, Twitter, Facebook and Instagram.
+... 2. Checking if Business have a working website.
+... 3. Google search results about company.
+... 4. Validating if company is blacklisted or not. (Assuming the business is going to start in Canada)
+
+Library RPA.Robocloud.Secrets
+Library RPA.Browser.Selenium
+Library RPA.FileSystem
+Library Collections
+Library RPA.Archive
+Library pdfGen.py
+Library RPA.HTTP
+Library RPA.PDF
+Library DateTime
+# -
+
+
+# Imported Libraries
+
+*** Variables ***
+${GLOBAL_RETRY_AMOUNT}= 3x
+${GLOBAL_RETRY_INTERVAL}= 1s
+${MAX_SEARCH_RESULT_CLICKABLE}= 3
+${RUNTIME_DIR}= ${CURDIR}${/}output${/}runtime
+${FINAL_DIR}= ${CURDIR}${/}output${/}result
+${SEARCH_ENGINE}= http://www.google.com
+${businessOperatingName}= AOT Technologies
+${businessWebsite}= http://www.aot-technologies.com
+${search-engine-url}= https://google.com
+${verify-bc-org-url}= https://www.orgbook.gov.bc.ca/en/home
+
+
+# +
+*** Keywords ***
+Gather The Social Media Profiles
+
+ New Pdf
+
+ Add New Heading Validate If Company Was Blacklisted
+
+ Validate If Company Was Blacklisted ${businessOperatingName}
+
+ Add New Page Search In Intranet About The Company
+
+ Search In Intranet About The Company ${businessOperatingName}
+
+ ${profiles} = Create List facebook linkedin twitter instagram
+
+ Search And Append Social Media Search Results ${businessOperatingName} ${profiles}
+
+ Add New Page Validate Company Website
+
+ Open And Validate Company Website ${businessWebsite}
+
+ Log To Console Checks completed
+
+ ${timestamp} = Get Current Date result_format=%Y%m%d%H%M%S
+
+ Save Pdf File ${FINAL_DIR}${/}data.pdf
+
+Search And Append Social Media Search Results
+ [Arguments] ${businessOperatingName} ${profiles}
+ Log To Console Started : Searching in facebook for company profiles
+ Open Available Browser ${search-engine-url}
+ Agree To Google Terms
+ FOR ${profile} IN @{profiles}
+ Input Text //input[@title="Search"] ${businessOperatingName} ${profile}
+ Submit Form
+ Wait Until Element Is Visible //div[@id="center_col"]
+ Take a screenshot of the page //div[@id="center_col"] ${profile}
+ Add New Page ${profile}
+ Add Image File ${RUNTIME_DIR}${/}${profile}.png
+ Go Back
+ END
+ Log To Console Completed : Searching in facebook for company profiles
+ [Teardown] Close Intranet Browser
+
+Search In Intranet About The Company
+ [Arguments] ${businessOperatingName}
+ Log To Console Started : Searching in intranet about the company
+ Open Available Browser ${search-engine-url}
+ Agree To Google Terms
+ Input Text //input[@title="Search"] ${businessOperatingName}
+ Submit Form
+ Wait Until Element Is Visible //div[@id="rcnt"]
+ Take a screenshot of the page //div[@id="rcnt"] searchResults
+ Add Image File ${RUNTIME_DIR}${/}searchResults.png
+ Log To Console Completed : Searching in intranet about the company
+ [Teardown] Close Intranet Browser
+
+Open And Validate Company Website
+ [Arguments] ${businessWebsite}
+ Log To Console Started : Open And Validate Company Website
+ ${length}= Get Length ${businessWebsite}
+ ${fileName}= Set Variable ${RUNTIME_DIR}${/}companyWebsite.png
+ IF ${length} > 0
+ Open Available Browser ${businessWebsite}
+ Capture Page Screenshot ${fileName}
+ Add Image File ${fileName}
+ END
+ Log To Console End : Open And Validate Company Website
+ [Teardown] Close Intranet Browser
+
+Validate If Company Was Blacklisted
+ [Arguments] ${businessOperatingName}
+ Log To Console Started : Validate If Company Was Blacklisted
+ Open Available Browser ${search-engine-url}
+ Agree To Google Terms
+ Input Text //input[@title="Search"] ${businessOperatingName}
+ Submit Form
+ Take a screenshot of the page //div[@class="GyAeWb"] blacklistdata
+ Add Image File ${RUNTIME_DIR}${/}blacklistdata.png
+ Log To Console End : Validate If Company Was Blacklisted
+ [Teardown] Close Intranet Browser
+
+Take a screenshot of the page
+ [Arguments] ${webEl} ${name}
+ Wait Until Page Contains Element ${webEl} timeout=20
+ Screenshot ${webEl} ${RUNTIME_DIR}${/}${name}.png
+
+Agree To Google Terms
+ ${res}= Does Page Contain Button I agree
+ IF ${res} == True
+ Click Button I agree
+ END
+
+Remove and create directory
+ [Arguments] ${DIR}
+ Remove Directory ${DIR} True
+ Create Directory ${DIR}
+
+Cleanup directory
+ ${RUNTIME}= Does Directory Exist ${RUNTIME_DIR}
+ ${FINAL}= Does Directory Exist ${FINAL_DIR}
+ Run Keyword If '${RUNTIME}'==True Remove and create directory ${RUNTIME} ELSE Create Directory ${RUNTIME_DIR}
+ Run Keyword If '${FINAL}'==True Remove and create directory ${FINAL} ELSE Create Directory ${FINAL_DIR}
+
+Close Intranet Browser
+ Close Browser
+# -
+
+
+*** Tasks ***
+Do Background check for the New Business Application
+ Cleanup directory
+ Gather The Social Media Profiles
+ [Teardown] Close Intranet Browser
diff --git a/rpa-robocorp-extention/robocorp-examples/background-check-robot/vault.json b/rpa-robocorp-extention/robocorp-examples/background-check-robot/vault.json
new file mode 100644
index 00000000..c28f72ad
--- /dev/null
+++ b/rpa-robocorp-extention/robocorp-examples/background-check-robot/vault.json
@@ -0,0 +1,6 @@
+{
+ "my_vault": {
+ "search-engine-url" : "https://google.com",
+ "verify-bc-org-url" : "https://www.orgbook.gov.bc.ca/en/home"
+ }
+}
\ No newline at end of file