I am trying to connect to the AWS device farm for desktop browser testing with Selenium 4 through a cooperate proxy, with no luck.
Here is the code I am using:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>My Selenium Tests</groupId>
<artifactId>Selenium</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>2.17.209</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- TestNG -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.6.0</version>
<scope>compile</scope>
</dependency>
<!-- AWS -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
<version>2.17.209</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>devicefarm</artifactId>
<version>2.17.209</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>aws-sdk-java</artifactId>
<version>2.17.209</version>
</dependency>
<!-- Selenium -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.2.2</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-remote-driver</artifactId>
<version>4.2.2</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.10.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
</project>
SeleniumTest.java
package Selenium;
import okhttp3.*;
import okhttp3.Authenticator;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.remote.HttpCommandExecutor;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.remote.http.ClientConfig;
import org.openqa.selenium.remote.http.HttpClient;
import org.openqa.selenium.remote.http.HttpClient.Factory;
import org.testng.annotations.Test;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.http.apache.ProxyConfiguration;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.devicefarm.DeviceFarmClient;
import software.amazon.awssdk.services.devicefarm.model.CreateTestGridUrlRequest;
import software.amazon.awssdk.services.devicefarm.model.CreateTestGridUrlResponse;
import java.io.IOException;
import java.net.*;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
public class SeleniumTest {
#Test
public void search() throws MalformedURLException, URISyntaxException, UnknownHostException {
ProxyConfiguration.Builder proxyConfiguration = ProxyConfiguration.builder();
proxyConfiguration.endpoint(new URI("http://myProxyUrl:8080"));
proxyConfiguration.username("myProxyUsername");
proxyConfiguration.password("myProxyPassword");
ApacheHttpClient.Builder httpClientBuilder = ApacheHttpClient.builder().proxyConfiguration(proxyConfiguration.build());
String projectARN = "arn:aws:devicefarm:someString:testgrid-project:someUid";
DeviceFarmClient deviceFarmClient = DeviceFarmClient.builder()
.credentialsProvider(DefaultCredentialsProvider.create())
.httpClientBuilder(httpClientBuilder)
.overrideConfiguration(ClientOverrideConfiguration.builder().build())
.region(Region.US_WEST_2)
.build();
CreateTestGridUrlRequest testGridUrlrequest = CreateTestGridUrlRequest.builder()
.expiresInSeconds(300)
.projectArn(projectARN)
.build();
CreateTestGridUrlResponse response = deviceFarmClient.createTestGridUrl(testGridUrlrequest);
URL testGridUrl = new URL(response.url());
Authenticator proxyAuthenticator = new Authenticator() {
public Request authenticate(Route route, Response response) throws IOException {
String credential = Credentials.basic("myProxyUsername", "myProxyPassword");
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
}
};
Builder okHttpClientBuilder = new Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("myProxyUrl", 8080)))
.proxyAuthenticator(proxyAuthenticator);
FirefoxOptions browserOptions = new FirefoxOptions();
browserOptions.addPreference("network.proxy.type", 0);
Factory factory = new MyHttpClientFactory(new OkHttpClient(okHttpClientBuilder));
HttpCommandExecutor executor = new HttpCommandExecutor(new HashMap<>(), testGridUrl, factory);
RemoteWebDriver remoteWebDriver = new RemoteWebDriver(executor, browserOptions);
remoteWebDriver.navigate().to("https://www.google.de");
remoteWebDriver.quit();
}
public class MyHttpClientFactory implements HttpClient.Factory {
final OkHttpClient okHttpClient;
public MyHttpClientFactory(OkHttpClient okHttpClient) {
this.okHttpClient = okHttpClient;
}
#Override
public HttpClient createClient(URL url) {
return (HttpClient) okHttpClient;
}
#Override
public HttpClient createClient(ClientConfig config) {
return (HttpClient) okHttpClient;
}
#Override
public void cleanupIdleClients() {
}
}
}
As you can see, I used the ApacheHttpClient as HTTPClient for the AWS DeviceFarmClient, so I can authenticate with the cooperate proxy - which works like a charm. I get the testGridUrl, which I can use in the second step to send commands via the RemoteWebDriver to the AWS Desktop Browser.
This is where the problems start. I use the OkHttpClient (recommended by Selenium) as HTTPClient for the RemoteWebDriver - here also to enable proxy authentication (which I think is not possible with the built in HTTPClient).
But when the test is run, I get a class cast exception - it seems the okHttpCLient cannot be cast to HttpClient.
java.lang.ClassCastException: class okhttp3.OkHttpClient cannot be cast to class org.openqa.selenium.remote.http.HttpClient (okhttp3.OkHttpClient and org.openqa.selenium.remote.http.HttpClient are in unnamed module of loader 'app')
at Selenium.SeleniumTest$MyHttpClientFactory.createClient(SeleniumTest.java:103)
at org.openqa.selenium.remote.HttpCommandExecutor.<init>(HttpCommandExecutor.java:107)
at org.openqa.selenium.remote.HttpCommandExecutor.<init>(HttpCommandExecutor.java:94)
at Selenium.SeleniumTest.search(SeleniumTest.java:77)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.testng.internal.invokers.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:139)
at org.testng.internal.invokers.TestInvoker.invokeMethod(TestInvoker.java:677)
at org.testng.internal.invokers.TestInvoker.invokeTestMethod(TestInvoker.java:221)
at org.testng.internal.invokers.MethodRunner.runInSequence(MethodRunner.java:50)
at org.testng.internal.invokers.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:962)
at org.testng.internal.invokers.TestInvoker.invokeTestMethods(TestInvoker.java:194)
at org.testng.internal.invokers.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:148)
at org.testng.internal.invokers.TestMethodWorker.run(TestMethodWorker.java:128)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
at org.testng.TestRunner.privateRun(TestRunner.java:806)
at org.testng.TestRunner.run(TestRunner.java:601)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:433)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:427)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:387)
at org.testng.SuiteRunner.run(SuiteRunner.java:330)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:95)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1256)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1176)
at org.testng.TestNG.runSuites(TestNG.java:1099)
at org.testng.TestNG.run(TestNG.java:1067)
Did anyone use a proxy for connection to the AWS DeviceFarm before with Selenium 4 and can give me some hints on how to procede (replace the HTTPClient? Cast possible?)
All the best, Pita.
Related
Any one know why when I run mvn clean test -Dos=android it doesn't find any tests to run? It runs find if I use the built in Cucumber or JUnit runner in Intellij. I am using Appium and Java 8.
Here are my files and folder structure
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>App</groupId>
<artifactId>Mobile-Automation</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>8.3.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.10.1</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-core</artifactId>
<version>7.10.1</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>gherkin</artifactId>
<version>26.0.1</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>7.10.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
CucumberTestRunner.java
package runner;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;
#RunWith(Cucumber.class)
#CucumberOptions(features = "classpath:features", glue = {"stepdefs"}, plugin = {"pretty", "html:target/cucumber-html-report.html"})
public class CucumberTestRunner {
public CucumberTestRunner(String[] args){}
}
TestBase.java
package runner;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import pageobjects.LoginPage;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Properties;
public class TestBase {
public static AppiumDriver driver;
public static LoginPage loginPage;
static Properties props = new Properties();
public static void androidSetUp() throws MalformedURLException {
try (InputStream inputStream = ClassLoader.getSystemResourceAsStream("decivecapabilities.properties")){
props.load(inputStream);
} catch (Exception e) {e.printStackTrace();}
DesiredCapabilities androidCaps = new DesiredCapabilities();
androidCaps.setCapability("deviceName", props.getProperty("android.capability.deviceName"));
androidCaps.setCapability("udid", props.getProperty("android.capability.udid"));
androidCaps.setCapability("platformName", props.getProperty("android.capability.platformName"));
androidCaps.setCapability("platformVersion", props.getProperty("android.capability.platformVersion"));
androidCaps.setCapability("automationName", props.getProperty("android.capability.automationName"));
androidCaps.setCapability("appPackage", props.getProperty("android.capability.appPackage"));
androidCaps.setCapability("appActivity", props.getProperty("android.capability.appActivity"));
driver = new AndroidDriver(new URL("http://localhost:4723/wd/hub"), androidCaps);
}
public static void iosSetUp() throws MalformedURLException {
try (InputStream inputStream = ClassLoader.getSystemResourceAsStream("decivecapabilities.properties")){
props.load(inputStream);
} catch (Exception e) {e.printStackTrace();}
DesiredCapabilities iosCaps = new DesiredCapabilities();
iosCaps.setCapability("deviceName", props.getProperty("ios.capability.deviceName"));
iosCaps.setCapability("udid", props.getProperty("ios.capability.udid"));
iosCaps.setCapability("platformName", props.getProperty("ios.capability.platformName"));
iosCaps.setCapability("platformVersion", props.getProperty("ios.capability.platformVersion"));
iosCaps.setCapability("automationName", props.getProperty("ios.capability.automationName"));
//iosCaps.setCapability("app", "app-path");
driver = new IOSDriver(new URL("http://localhost:4723/wd/hub"), iosCaps);
}
public static void pageObjectInit(){
loginPage = new LoginPage(driver);
}
public static void tearDown(){
if (driver != null){
driver.quit();
}
}
}
Any insight is appreciated, and I can add post more files if needed.
By default, the Surefire Plugin will automatically include all test classes with the following wildcard patterns:
**/Test*.java - includes all of its subdirectories and all Java filenames that start with "Test".
**/*Test.java - includes all of its subdirectories and all Java filenames that end with "Test".
**/*Tests.java - includes all of its subdirectories and all Java filenames that end with "Tests".
**/*TestCase.java - includes all of its subdirectories and all Java filenames that end with "TestCase".
https://maven.apache.org/surefire/maven-surefire-plugin/examples/inclusion-exclusion.html
By convention tests executed by Maven should end with "Test". So your class should be named RunCucumberTest.
I'm creating an application with spring boot and I want to authenticate my application using JWT but it is in microservice. In the JWT service it runs normally but I want to authenticate all the microservice services so I put the JWT service code for the api-gateway but because it uses spring-cloud-starter-gateway I can't use spring-boot-starter-web so far everything well. My big problem is that when I run some service, a login page appears. I wanted to remove this page does anyone have any idea how to do this?
Here is my application code below:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>routing</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>routing</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2021.0.4</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>3.1.4</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>3.1.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties:
server.port=8080
spring.application.name=routing
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
eureka.instance.hostname=localhost
spring.cloud.gateway.discovery.locator.enabled=true
rsa.publickey=classpath:carts/public.pem
rsa.privatekey=classpath:carts/private.pem
#spring.main.web-application-type=reactive
spring.cloud.gateway.enabled=true
spring.cloud.gateway.routes[0].id=user
spring.cloud.gateway.routes[0].uri=lb://USER
spring.cloud.gateway.routes[0].predicates=Path=/user/**
spring.cloud.gateway.routes[1].id=testes
spring.cloud.gateway.routes[1].uri=lb://TESTES
spring.cloud.gateway.routes[1].predicates=Path=/testes/**
spring.cloud.gateway.routes[2].id=user-create
spring.cloud.gateway.routes[2].uri=lb://USER-CREATE
spring.cloud.gateway.routes[2].predicates=Path=/user-create/**
spring.cloud.gateway.routes[3].id=jwt
spring.cloud.gateway.routes[3].uri=lb://JWT
spring.cloud.gateway.routes[3].predicates=Path=/**
RsaKeyPropreties.java:
package com.example.routing.config;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import org.springframework.boot.context.properties.ConfigurationProperties;
#ConfigurationProperties(prefix = "rsa")
public record RsaKeyPropreties(RSAPublicKey publickey,RSAPrivateKey privatekey) {
}
SecurityConfig.java:
package com.example.routing.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import com.nimbusds.jose.proc.SecurityContext;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import static org.springframework.security.config.Customizer.withDefaults;
import org.springframework.boot.autoconfigure.AutoConfiguration;
#Configuration
#AutoConfiguration
#EnableWebSecurity
public class SecurityConfig {
private final RsaKeyPropreties Rsakeys;
public SecurityConfig(RsaKeyPropreties Rsakeys) {
this.Rsakeys = Rsakeys;
}
#Bean
public InMemoryUserDetailsManager user(){
return new InMemoryUserDetailsManager(
User.withUsername("username")
.password("{noop}password")
.authorities("read")
.build()
);
}
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
return http
.csrf(csrf->csrf.disable())
//.authorizeRequests(auth->auth.antMatchers("/user**").authenticated())
.authorizeRequests(auth->auth.anyRequest().authenticated())
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.sessionManagement(session->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.httpBasic(withDefaults()).build();
}
#Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withPublicKey(Rsakeys.publickey()).build();
}
#Bean
JwtEncoder jwtEncoder() {
JWK jwk = new RSAKey.Builder(Rsakeys.publickey()).privateKey(Rsakeys.privatekey()).build();
JWKSource<SecurityContext> jws = new ImmutableJWKSet<>(new JWKSet(jwk));
return new NimbusJwtEncoder(jws);
}
}
RoutingApplication.java:
package com.example.routing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import com.example.routing.config.RsaKeyPropreties;
#EnableConfigurationProperties(RsaKeyPropreties.class)
#SpringBootApplication
#EnableDiscoveryClient
public class RoutingApplication {
public static void main(String[] args) {
SpringApplication.run(RoutingApplication.class, args);
}
}
Thanks!
For Servlet app, this will return 401 (unauthorized) instead of 302 (redirect to login) when authorization is missing or invalid:
http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
});
For a reactive app, you can provide a ServerAccessDeniedHandler bean instead:
#Bean
ServerAccessDeniedHandler serverAccessDeniedHandler() {
return (var exchange, var ex) -> exchange.getPrincipal().flatMap(principal -> {
final var response = exchange.getResponse();
response.setStatusCode(principal instanceof AnonymousAuthenticationToken ? HttpStatus.UNAUTHORIZED : HttpStatus.FORBIDDEN);
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
final var dataBufferFactory = response.bufferFactory();
final var buffer = dataBufferFactory.wrap(ex.getMessage().getBytes(Charset.defaultCharset()));
return response.writeWith(Mono.just(buffer)).doOnError(error -> DataBufferUtils.release(buffer));
});
}
Spring-boot starters I maintain here (which are thin wrappers arround spring-boot-starter-oauth2-resource-server) are doing that by default plus a few other usefull things:
map authorities from a list of claims of your choice (giving you hand on case and prefix)
stateless session-management (like you do)
disabled CSRF (only if session-management is left stateless)
fine grained CORS config from properties
multi-tenancy (accept more than just one JWT issuer)
As a side note, what about having your gateway being a pass-through for OAuth2 (just forward requests authorization header and responses HTTP status) and implement resources access-control (spring-security .authorizeRequests() and #PreAuthorize rules) on resource-server, where you can unit-test it?
I have a test project where I'm trying to setup e2e api tests using rest-assured. Tests run fine if I run them from the feature files, however, when I try to run them with maven, 0 tests run. I believe there is something funky with my pom.xml but I can't figure it out...
My project structure looks like:this
My pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.0</version>
</parent>
<artifactId>qa-automation-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<groupId>com</groupId>
<packaging>jar</packaging>
<properties>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-bom</artifactId>
<version>7.2.3</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<version>1.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit-platform-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-path</artifactId>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>xml-path</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<properties>
<configurationParameters>
cucumber.junit-platform.naming-strategy=long
</configurationParameters>
</properties>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
My Application.java
package com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
#PropertySources({
#PropertySource("classpath:application.properties")
})
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
My CucumberSpringConfiguration.class
import io.cucumber.spring.CucumberContextConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import com.Application;
#CucumberContextConfiguration
#SpringBootTest(classes = Application.class)
public class CucumberSpringConfiguration {
}
My CucumberTest.java
import org.junit.platform.suite.api.ConfigurationParameter;
import org.junit.platform.suite.api.IncludeEngines;
import org.junit.platform.suite.api.SelectClasspathResource;
import org.junit.platform.suite.api.Suite;
import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;
#Suite
#IncludeEngines("cucumber")
#SelectClasspathResource("src/test/resources/example")
#ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "com.example")
public class CucumberTest {
}
I'm not really familiar with Spring though so I'm pretty sure I'm not using it correctly in my ApiTestStepDef.java
package com.example;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import io.cucumber.java.en.Given;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.Assertions;
import org.springframework.beans.factory.annotation.Autowired;
import com.client.RestAssuredClient;
import com.model.User;
import com.utils.Helper;
import static io.restassured.RestAssured.given;
public class ApiTestStepDef {
private Response response;
private RequestSpecification request;
private User user;
private User responseBody;
#Autowired
private RestAssuredClient restAssuredClient;
#Given("{string} endpoint")
public void setBaseUsersURI(String url){
request =
given().log().all().
spec(restAssuredClient.createReqSpec(url));
}
#When("user posts request with details {string} {string} {string}")
public void sendRequest(String name, String gender, String status){
user = new User(name, gender, Helper.createRandomEmail(), status);
response =
request.given().log().all().
body(user).
when().
post().
then().log().all().
extract().response();
}
#Then("response status code is {int} and response contains correct user details")
public void checkResponseStatusCode(int statusCode){
response.then().spec(restAssuredClient.createResSpec(statusCode));
responseBody = response.getBody().as(User.class);
Assertions.assertEquals(user.getGender(), responseBody.getGender());
Assertions.assertEquals(user.getStatus(), responseBody.getStatus());
Assertions.assertEquals(user.getEmail(), responseBody.getEmail());
Assertions.assertEquals(user.getName(), responseBody.getName());
}
}
And RestAssuredClient.java
package com.client;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import static io.restassured.RestAssured.oauth2;
#Component
public class RestAssuredClient {
#Value("${access.token}")
private String accessToken;
#Value("${base.uri}")
private String baseUri;
public ResponseSpecification createResSpec(int statusCode){
return
new ResponseSpecBuilder()
.expectStatusCode(statusCode)
.expectContentType(ContentType.JSON)
.build();
}
public RequestSpecification createReqSpec(String url){
return new RequestSpecBuilder()
.setBaseUri(baseUri)
.setContentType(ContentType.JSON)
.setAuth(oauth2(accessToken))
.setBasePath(url)
.build();
}
}
#SelectClasspathResource("src/test/resources/example")
Typically src/test/resources is not part of the classpath.
After running mvn test have a look at target/test-classes to understand the structure of what is on the classpath.
I implement ElasticSearchConsumer class program which is supposed to return the Id document. The running program desplay a warning message and return just twitter :
déc. 23, 2020 9:41:10 AM org.elasticsearch.client.RestClient logResponse
WARNING: request [POST https://kafka-course-6054260476.us-east-1.bonsaisearch.net:443/twitter/tweets?timeout=1m] returned 1 warnings: [299 Elasticsearch-7.2.0-508c38a "[types removal] Specifying types in document index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, or /{index}/_create/{id})."]
[main] INFO com.gihub.simplesteph.kafka.tutorial3.ElasticSearchConsumer - twitter
Process finished with exit code 0
This is the code :
package com.gihub.simplesteph.kafka.tutorial3;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class ElasticSearchConsumer {
public static RestHighLevelClient createClient(){
// replace with your own credentials
String hostname = "kafka-course-6054260476.us-east-1.bonsaisearch.net";
String username = "48h3frssnm";
String password = "8iliybmly0";
//don't do if you run a local ES
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
RestClientBuilder builder = RestClient.builder(
new HttpHost(hostname, 443, "https"))
.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
#Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpAsyncClientBuilder) {
return httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
}
});
RestHighLevelClient client = new RestHighLevelClient(builder);
return client;
}
public static void main(String[] args) throws IOException {
Logger logger = LoggerFactory.getLogger(ElasticSearchConsumer.class.getName());
RestHighLevelClient client = createClient();
String jsonString = "{ \"foo\": \"bar\" }";
IndexRequest indexRequest = new IndexRequest("twitter", "tweets" ).source(jsonString, XContentType.JSON);
IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
String id = indexResponse.getIndex();
logger.info(id);
//close the client gracefully
client.close();
}
}
This is the pom.xml file :
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>kafka-beginners-course</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>kafka-consumer-elasticsearch</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.elasticsearch.client/elasticsearch-rest-high-level-client -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.10.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.10.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-clients -->
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>2.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-simple -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
<!-- <scope>test</scope>-->
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
<version>4.1.4</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore-nio</artifactId>
<version>4.4.14</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.14</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</project>
You can use likewise its removed warning message.
IndexRequest indexRequest = new IndexRequest("twitter", "_doc",tweetId).
The warning does not related to what returned, it is related to deprecated constructor for IndexRequest() and you can ignore it or should not pass type argument(second argument) to constructor.
you retrieved the index name by getIndex() method but you should retrieve the Id by getId() method.
I made two classes:
Main class with embedded Tomcat(8.5.20)
ServerEndpoint of Websocket
I run the main class on IntelliJ IDEA
and run this JavaScript: new WebSocket('ws://localhost:8080/ws')
in the console of Google Chrome.
I expected the response code is 200, but actually it is 404.
How can I fix this?
Main class:
package webapp;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
import javax.servlet.ServletException;
import java.io.File;
public class Main {
private static final String STATIC_DIR = "src/main/static/";
public static void main(String[] args) throws ServletException, LifecycleException {
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);
File staticDir = new File(STATIC_DIR);
tomcat.addWebapp("/", staticDir.getAbsolutePath());
tomcat.start();
tomcat.getServer().await();
}
}
ServerEndpoint:
package webapp.websocket;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
#ServerEndpoint("/ws")
public class SampleWebSocket {
#OnOpen
public void onOpen(Session session) {
System.out.println("open");
}
#OnClose
public void onClose(Session session) {
System.out.println("close");
}
#OnMessage
public String onMessage(String text) {
System.out.println("message:" + text);
return "Server:" + text;
}
}
Thank you.
I found a solution.
This question is dupricated.
I read Got 404 error on tomcat 7.0.47 websocket and editted my pom.xml.
Following is the whole of my pom.xml.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>websocket-sample</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>websocket-sample</name>
<properties>
<java.version>1.8</java.version>
<maven.compiler.target>${java.version}</maven.compiler.target>
<maven.compiler.source>${java.version}</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.20</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-websocket</artifactId>
<version>8.5.20</version>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
</project>
Just add the following dependency to pom.xml
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-websocket</artifactId>
<version>8.5.20</version>
</dependency>
What solved the issue for me was switching from the javax.websocket-api dependency to the Tomcat-specific tomcat-embed-websocket dependency instead and switching all my imports (ServerEndpoint, onMessage, etc.) to the Tomcat-specific jakarta.websocket.* packages instead of the javax.websocket.x packages.
Here is the endpoint code that worked for me -- notice that there are no javax.websocket imports and instead I have jakarta.websocket imports:
import java.io.IOException;
import jakarta.websocket.EncodeException;
import jakarta.websocket.OnMessage;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;
#ServerEndpoint("/echo")
public class EchoEndpoint {
// Implementation.
#OnMessage
public void onMessage(
Session session,
String message)
throws
IOException,
EncodeException {
// Send the same message back.
session.getBasicRemote().sendText(message);
}
}
And my pom.xml file dependencies:
<!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>10.1.0-M8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-websocket -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<version>10.1.0-M8</version>
</dependency>
As far as I can tell, the pom.xml should not contain the javax.websocket dependency at all, to ensure there are no collisions/clashes between that one and the Tomcat-specific tomcat-embed-websocket dependency.