Feign does not resolve name - java

I am trying t call another service using Spring Cloud's Open Feign but here is the response I keep getting:
{
"timestamp": 1579015052962,
"status": 500,
"error": "Internal Server Error",
"message": "auth-service: Name or service not known executing GET http://auth-service/api/v1/auth",
"path": "/api/v1/event"
}
Here's my code:
package com.eventmanager.events.client;
import com.eventmanager.events.client.mappings.Auth;
import com.eventmanager.events.config.CustomFeignClientConfig;
import com.eventmanager.events.responses.Response;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
#FeignClient(name = "auth-service", configuration = CustomFeignClientConfig.class)
public interface AuthClient {
#GetMapping("/api/v1/auth")
public Response<Auth> getLoggedUser(#RequestHeader(value = "Authorization") String authorization);
}
I configured Feign to use the OkHttp client and I'm not sure if it's responsible for the error:
package com.eventmanager.events.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import feign.okhttp.OkHttpClient;
#Configuration
public class CustomFeignClientConfig {
#Bean
public OkHttpClient client() {
return new OkHttpClient();
}
}

If you are using Finchey.SR1, You can check this https://stackoverflow.com/a/52727544
There seems to be an issue with ContentPath in that cloud version.

May be because you have not specified base URL. for client & it is taking base URL as auth-service.
#FeignClient(name = "auth-service", configuration = CustomFeignClientConfig.class, url = "http://lcoalhost:8080")
public interface AuthClient {
#GetMapping("/api/v1/auth")
public Response<Auth> getLoggedUser(#RequestHeader(value = "Authorization") String authorization);
}

Guess I'm a little late to the party. The following dependency can be added to your pom.xml file. If you you have feign-okhttp dependency then you should be having this dependency by default.
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>5.0.0-alpha.6</version>
</dependency>
Now instead of importing feign.okhttp.OkHttpClient; import okhttp3.OkHttpClient.
So your class should look like below.
package com.eventmanager.events.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import okhttp3.OkHttpClient;
#Configuration
public class CustomFeignClientConfig {
#Bean
public OkHttpClient client() {
return new OkHttpClient();
}
}
Also consider adding the below entry in your application.properties file. This will stop Feign from using the default http client.
feign.httpclient.enabled=false
You may refer to this post for more information

Related

Unable to run JUnit5 PACT test. No method annotated with #Pact was found on test class ConsumerContractTest for provider ''

I'm trying to get a PACT test running on JUnit5. We use JUnit4 for others, but this one will be JUnit5. The error occurs when running the JUnit5 test using the pact annotation on the RequestResponsePact method.
Error : No method annotated with #Pact was found on test class ConsumerContractTest for provider ''.
I've seen Basic Pact/Junit5 Test Setup fails. No method annotated with #Pact was found for provider error, but this is issue was due to the #PactTestFor(pactMethod = "examplePact") not matching the #Pact method name. But on my code it does match.
I can't seem to figure out why I get the error and especially why the error has an empty provider(provider '') despite defining one("some-provider").
Example code :
import au.com.dius.pact.consumer.MockServer
import au.com.dius.pact.consumer.Pact
import au.com.dius.pact.consumer.dsl.PactDslJsonArray
import au.com.dius.pact.consumer.dsl.PactDslWithProvider
import au.com.dius.pact.consumer.junit5.PactConsumerTestExt
import au.com.dius.pact.consumer.junit5.PactTestFor
import au.com.dius.pact.model.RequestResponsePact
import groovyx.net.http.RESTClient
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.http.HttpStatus
#ExtendWith(PactConsumerTestExt.class)
class ConsumerContractTest {
#Pact(consumer = "some-consumer", provider = "some-provider")
RequestResponsePact examplePact(PactDslWithProvider builder) {
builder
.given("provider state")
.uponReceiving("Contract description")
.method("GET")
.matchPath("/endpoint")
.willRespondWith()
.status(200)
.headers(["Content-Type": "application/vnd.pnf.v1+json"])
.body(new PactDslJsonArray())
.toPact()
}
#Test
#PactTestFor(pactMethod = "examplePact")
void exampleTest(MockServer mockServer) {
def client = new RESTClient(mockServer.getUrl())
}
}
Not sure if that's just the gist you've posted here but I see the return word missing and also the #PactTestFor annotation missing the provider and version. Here is an example I have that works for my project.
import au.com.dius.pact.consumer.dsl.DslPart;
import au.com.dius.pact.consumer.dsl.PactDslJsonBody;
import au.com.dius.pact.consumer.dsl.PactDslWithProvider;
import au.com.dius.pact.consumer.junit5.PactConsumerTestExt;
import au.com.dius.pact.consumer.junit5.PactTestFor;
import au.com.dius.pact.core.model.PactSpecVersion;
import au.com.dius.pact.core.model.RequestResponsePact;
import au.com.dius.pact.core.model.annotations.Pact;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import java.util.HashMap;
import java.util.Map;
import static com.example.mbbackend.config.Constants.*;
import static com.example.mbbackend.util.Utils.getRequestSpecification;
import static org.junit.jupiter.api.Assertions.assertEquals;
#ExtendWith(PactConsumerTestExt.class)
class GetActorIT {
Map<String, String> headers = new HashMap<>();
String path = "/api/mb/actor/";
#Pact(provider = PACT_PROVIDER, consumer = PACT_CONSUMER)
public RequestResponsePact createPact(PactDslWithProvider builder) {
headers.put("Content-Type", "application/json");
DslPart bodyReturned = new PactDslJsonBody()
.uuid("id", "1bfff94a-b70e-4b39-bd2a-be1c0f898589")
.stringType("name", "A name")
.stringType("family", "A family")
.stringType("imageUrl", "http://anyimage.com")
.close();
return builder
.given("A request to retrieve an actor")
.uponReceiving("A request to retrieve an actor")
.pathFromProviderState(path + "${actorId}", path + "1bfff94a-b70e-4b39-bd2a-be1c0f898589")
.method("GET")
.headers(headers)
.willRespondWith()
.status(200)
.body(bodyReturned)
.toPact();
}
#Test
#PactTestFor(providerName = PACT_PROVIDER, port = PACT_PORT, pactVersion = PactSpecVersion.V3)
void runTest() {
//Mock url
RequestSpecification rq = getRequestSpecification().baseUri(MOCK_PACT_URL).headers(headers);
Response response = rq.get(path + "1bfff94a-b70e-4b39-bd2a-be1c0f898589");
assertEquals(200, response.getStatusCode());
}
}

Migrate Feign Load Balancer implementation to compatible with Spring cloud 2020.0.0

I have below implementation of Feign Load balancer which is working with spring cloud Hoxtan SR6 dependencies.
import feign.auth.BasicAuthRequestInterceptor;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.annotation.Bean;
public class ClientConfig {
#Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor(
#Value("${username}") String username,
#Value("${password}") String password) {
return new BasicAuthRequestInterceptor(username, password);
}
#Autowired
private CachingSpringLoadBalancerFactory cachingFactory;
#Autowired
private SpringClientFactory clientFactory;
#Value("${keystore.location}")
private String keyStoreLocation;
#Value("${keystore.secPhase}")
private String keyPassword;
#Bean
public Client feignClient() {
SslUtils.KeystoreConfig truststoreConfig = SslUtils.KeystoreConfig.builder().type("JKS").location(keyStoreLocation).password(keyPassword).build();
SocketFactory factory = new SocketFactory(() -> SslUtils.newContext(null, truststoreConfig));
NoopHostnameVerifier verifier = new NoopHostnameVerifier();
Client.Default client = new Client.Default(factory, verifier);
return new LoadBalancerFeignClient(client, cachingFactory, clientFactory);
}
}
I tried to upgrade spring cloud version to 2020.0.0. I noticed below packages no longer available.
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
How can I change the current implementation? or what dependency will provide these packages?
I was experiencing the same issue.
Finally, I solve the error by adding the bellow dependency.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>

Spring WebFlux File Upload: Unsupported Media Type 415 with Multipart upload

I'm running into some issues handling a file upload using spring's reactive framework. I think I'm following the docs, but can't get away from this 415 / Unsupported Media Type issue.
My controller looks like below (as per the example here: https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-multipart-forms)
package com.test.controllers;
import reactor.core.publisher.Flux;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.codec.multipart.Part;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class TestController {
#RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public Flux<String> uploadHandler(#RequestBody Flux<Part> parts) {
return parts
.filter(part -> part instanceof FilePart)
.ofType(FilePart.class)
.log()
.flatMap(p -> Flux.just(p.filename()));
}
}
POSTing to this endpoint though, always gives me the same output:
curl -X POST -F "data=#basic.ppt" http://localhost:8080/upload
---
"Unsupported Media Type","message":"Content type 'multipart/form-data;boundary=------------------------537139718d79303c;charset=UTF-8' not supported"
I've attempted to use #RequestPart("data") too, but get a similar Unsupported Media Type error, albeit with the content type of the file.
It seems that Spring is having issues converting these to a Part..? I'm stuck - any help is apprecitated!
Well, it's not a direct answer for your question, because I use functional endpoints, but I hope it will help you somehow.
import org.springframework.context.annotation.Bean;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.codec.multipart.Part;
import org.springframework.stereotype.Controller;
import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import java.io.File;
import java.util.Map;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
import static org.springframework.web.reactive.function.server.RouterFunctions.nest;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
#Controller
public class FileUploadController {
#Bean
RouterFunction<ServerResponse> apiRoutes() {
return nest(path("/api"),
route(POST("/upload"), fileUpload()));
}
private HandlerFunction<ServerResponse> fileUpload() {
return request -> {
return request.body(BodyExtractors.toMultipartData()).flatMap(parts -> {
Map<String, Part> map = parts.toSingleValueMap();
final FilePart filePart = (FilePart) map.get("file");
final String dir = "C:\\JDeveloper\\mywork\\Spring\\SpringTest\\webflux-file-upload\\uploaded";
filePart.transferTo(new File(dir + "/" + filePart.filename()));
return ServerResponse.ok().body(fromObject("ok, file uploaded"));
}
);
};
}
}
You can upload a file with curl like this:
curl -F "file=#C:\Users\Wojtek\Desktop\img-5081775796112008742.jpg" localhost:8080/api/fileupload
Thanks #kojot for your answer, but in this case I discovered the issue was my inclusion of spring-webmvc transiently in addition to spring-webflux. Your solution would likely have worked too, but I wanted to stick with the Controller style so ended up forcibly excluding spring-webmvc from my build.gradle:
configurations {
implementation {
exclude group: 'org.springframework', module: 'spring-webmvc'
}
}
After that it worked as documented.

Spring MultipartFile parameter not respecting configured maxFileSize

I have a file upload Controller. I'm trying to make the max file size configurable, but I'm not able to figure out why the configuration as documented (https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-multipart-file-upload-configuration) is not being applied.
plugins {
id 'org.springframework.boot' version '2.1.4.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.MultipartConfigElement;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
#Controller
public class FileUploadController {
private MultipartConfigElement multipartConfigElement;
#Autowired
public FileUploadController(MultipartConfigElement multipartConfigElement) {
this.multipartConfigElement = multipartConfigElement;
}
#PostMapping("/upload")
public void upload(#RequestParam("file") MultipartFile file) throws IOException {
InputStream inputStream = new BufferedInputStream(file.getInputStream());
// TODO something with inputStream
long fileSize = file.getSize();
boolean fileSizeLimitExceeded = fileSize > multipartConfigElement.getMaxFileSize();
return;
}
}
Debug screenshot
I expect the multipartConfigElement.getMaxFileSize() should prevent larger files getting this far and automatically return a 400 or some other type of exception.
But instead the maxFileSize seems to be completely ignored.
So it turns out that the limits do work, and do automatically throw Exceptions.
I saw this when I ran a request against my Controller using Postman.
{
"timestamp": "2019-04-05T09:52:39.839+0000",
"status": 500,
"error": "Internal Server Error",
"message": "Maximum upload size exceeded; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$FileSizeLimitExceededException: The field file exceeds its maximum permitted size of 1 bytes.",
"path": "/upload"
}
The reason I wasn't seeing that was because I was testing with MockMVC (snippet below). MockMVC doesn't seem to trigger the exceptions for some reason – maybe because it is not running on a compatible web server. Probably related to https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#spring-mvc-test-vs-end-to-end-integration-tests.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.io.FileInputStream;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#RunWith(SpringRunner.class)
#SpringBootTest(classes = DemoApplication.class)
#AutoConfigureMockMvc
public class FileUploadTest {
#Autowired
private MockMvc mockMvc;
#Test
public void givenAFileThatExceedsTheLimit_whenUploaded_responseWith400Error() throws Exception {
MockMultipartFile file =
new MockMultipartFile("file", new FileInputStream(TestUtils.loadLargeFile()));
this.mockMvc
.perform(MockMvcRequestBuilders.multipart("/upload").file(file)
.contentType(MediaType.MULTIPART_FORM_DATA_VALUE))
.andExpect(status().isBadRequest());
}
}
As commented by Andrew E, MockMvc won't replicate the max file size that you set with the properties spring.servlet.multipart.max-file-size and spring.servlet.multipart.max-request-size.
I managed to test the file size limite like this (adapted from here https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing.spring-boot-applications.with-running-server)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class RunningServerTest {
// my upload endpint requires authentication, so I need to mock the Oauth2 authorization server response
#MockBean private JwtDecoder jwtDecoder;
#Test
void shouldNotUploadMultipartFileGreaterThanMaxAllowedSize(#Autowired WebTestClient webClient) {
// GIVEN
Jwt jwt =
Jwt.withTokenValue("some-bearer-token")
.header("key", "value")
.claim("email", "user_email#example.com")
.build();
when(jwtDecoder.decode("some-bearer-token")).thenReturn(jwt);
ClassPathResource res = new ClassPathResource("file-bigger-than-max-size.txt");
// WHEN + THEN
webClient
.post()
.uri("/upload")
.header("Authorization", "Bearer some-bearer-token")
.header("Content-Type", MediaType.MULTIPART_FORM_DATA_VALUE)
.body(BodyInserters.fromResource(res))
.exchange()
.expectStatus()
// you might need to define a custom exception handler
// to map org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException
// to a 400 http error
.isEqualTo(HttpStatus.BAD_REQUEST);
}
}

The constructor TransportClient() is undefined?

I am working on elastic search using mongo db. When importing transport client it shows
The constructor TransportClient() is undefined.
I configured it as follows:
package com.appointment.api.mobile.config;
import javax.annotation.Resource;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
#Configuration
#PropertySource(value = "classpath:elasticsearch.properties")
#EnableElasticsearchRepositories(basePackages = "com.appointment.api")
public class ElasticsearchConfiguration {
#Resource
private Environment environment;
#Bean
public Client client() {
TransportClient client = new TransportClient();
/* TransportAddress address = new InetSocketTransportAddress(environment.getProperty("elasticsearch.host"), Integer.parseInt(environment.getProperty("elasticsearch.port")));
client.addTransportAddress(address); */
return client;
}
#Bean
public ElasticsearchOperations elasticsearchTemplate() {
return new ElasticsearchTemplate(client());
}
}
dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
Project:
Spring boot maven project.
According to the doc here, there is no public default constructor for the TransportClient , you need to do something like:
TransportClient client = new PreBuiltTransportClient(Settings.EMPTY)

Categories