Sleuth 3.0.1 + Spring Cloud Gateway = traceids do not correlate on request/response - spring-webflux

Well I have a new setup with Sleuth 3.0.1 where by default spring.sleuth.reactor.instrumentation-type is set to manual
Therefore I wrapped all my places where I need to trace with WebFluxSleuthOperators.withSpanInScope(tracer, currentTraceContext, exchange, () -> {}) logic and it generally puts span in scope but the traceid is always different before and after the response is received:
INFO [gateway,2b601780f2242aa9,2b601780f2242aa9] 9 --- [or-http-epoll-1] ...LogFilter : Request - Username: ..., method: GET, uri: ...
INFO [gateway,,] 9 --- [or-http-epoll-1] ...ValidationFilter : ... Validation passed
INFO [gateway,6a396715ff0979a6,6a396715ff0979a6] 9 --- [or-http-epoll-4] ...LogFilter : Request - Username: ..., method: GET, uri: ...
INFO [gateway,,] 9 --- [or-http-epoll-4] ...ValidationFilter : ... Validation passed
INFO [gateway,020c11ab0c37f8ae,020c11ab0c37f8ae] 9 --- [or-http-epoll-2] ...LogFilter : Request - Username: ..., method: GET, uri: ...
INFO [gateway,,] 9 --- [or-http-epoll-2] ...ValidationFilter : Validation passed
INFO [gateway,189a49553566f771,f51aaaf68e5738d0] 9 --- [or-http-epoll-1] ...LogFilter : Response - Status: 200 OK
INFO [gateway,92f25dcff9833079,87db5f7b1cbefedb] 9 --- [or-http-epoll-2] ...LogFilter : Response - Status: 200 OK
INFO [gateway,d8b133f2200e7808,1743df4b5ad37d07] 9 --- [or-http-epoll-1] ...LogFilter : Response - Status: 400 BAD_REQUEST
Digging deeper it seems the span returned on exchange.getAttribute(Span.class.getName()); has different trace ID in post routing phase.
Of course changing instrumentation-type to decorate_on_last fixes the issue but would like to avoid unnecessary performance degradations.

Related

Spring Cloud Reactor Sleuth - Not setting MDC with new span id when request is sent with WebClient

WebClient Initialisation
Code to call backend and initialisation as shown below. The sample code is using the spring-cloud-version Hoxton.SR4 , 'org.springframework.boot' version '2.3.1.RELEASE'. Full sample at https://github.com/malika15/sleuth-resttemplate-webclient-comparison
#EnableAutoConfiguration
#RestController
public class FrontendUsingWebClient {
#Autowired
WebClient webClient;
private Logger log = LoggerFactory.getLogger("FrontendUsingWebClient");
public static void main(String[] args) {
SpringApplication.run(FrontendUsingWebClient.class,
"--spring.application.name=FrontendUsingWebClient",
"--server.port=8081");
}
#Bean
WebClient webClient() {
return WebClient.builder().exchangeStrategies(ExchangeStrategies.builder().codecs(c ->
c.defaultCodecs().enableLoggingRequestDetails(true)).build()
).baseUrl("http://localhost:9000").build();
}
#RequestMapping("/")
public Mono<String> callBackend() {
log.info("Frontend WebClient::Begin");
Mono<String> response = webClient.get().uri("/api").retrieve().bodyToMono(String.class)
.doFirst(() -> log.info("Frontend WebClient ::doFirst"))
.doOnSubscribe(subscription -> log.info("Frontend WebClient::doOnSubscribe"))
.doOnRequest(value -> log.info("Frontend WebClient::doOnRequest"))
.doOnSuccess(s -> log.info("Frontend WebClient::doOnSuccess"))
.doOnEach(stringSignal -> log.info("Frontend WebClient::doOnEach"))
.doOnNext(s -> log.info("Frontend WebClient::doOnNext"))
.doAfterTerminate(() -> log.info("Frontend::doAfterTerminate"))
.doFinally(signalType -> log.info("Frontend WebClient::doFinally"))
.doOnCancel(() -> log.info("Frontend WebClient::doOnCancel"));
log.info("Frontend WebClient::End");
return response;
}
}
Webclient - New span id in HTTP headers but not in MDC
Notice in log output below, HTTP header has X-B3-SpanId:"e8c842b87aa2c176" where as MDC has d6737613f8c125f7,d6737613f8c125f7. Sleuth starts a new span for the webclient call and sets it on request X-B3 Headers but fails to set on the TraceContext(MDC)
2020-06-19 13:55:29.516 TRACE [FrontendUsingWebClient,d6737613f8c125f7,d6737613f8c125f7,false] 14008 --- [ctor-http-nio-3] o.s.w.r.f.client.ExchangeFunctions : [418bc045] HTTP GET http://localhost:9000/api, headers=[X-B3-TraceId:"d6737613f8c125f7", X-B3-SpanId:"e8c842b87aa2c176", X-B3-ParentSpanId:"d6737613f8c125f7", X-B3-Sampled:"0"]
RestTemplate - New span id in HTTP headers and in MDC
Notice in log output below new span being created Starting scope for span: 2d795955f8643a11/79ec6c794e86cd58 and set on MDC 2d795955f8643a11,79ec6c794e86cd58. Sleuth starts new span for RestTemplate call and closes after response is received. Setting it on MDC appropriately
2020-06-19 13:52:15.520 DEBUG [FrontendUsingRestTemplate,2d795955f8643a11,2d795955f8643a11,false] 12564 --- [ctor-http-nio-3] o.s.web.client.RestTemplate : HTTP GET http://localhost:9000/api
2020-06-19 13:52:15.524 DEBUG [FrontendUsingRestTemplate,2d795955f8643a11,2d795955f8643a11,false] 12564 --- [ctor-http-nio-3] o.s.web.client.RestTemplate : Accept=[text/plain, application/json, application/*+json, */*]
2020-06-19 13:52:15.525 TRACE [FrontendUsingRestTemplate,2d795955f8643a11,2d795955f8643a11,false] 12564 --- [ctor-http-nio-3] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'tracingClientHttpRequestInterceptor'
2020-06-19 13:52:15.526 TRACE [FrontendUsingRestTemplate,2d795955f8643a11,79ec6c794e86cd58,false] 12564 --- [ctor-http-nio-3] o.s.c.sleuth.log.Slf4jScopeDecorator : Starting scope for span: 2d795955f8643a11/79ec6c794e86cd58
2020-06-19 13:52:15.526 TRACE [FrontendUsingRestTemplate,2d795955f8643a11,79ec6c794e86cd58,false] 12564 --- [ctor-http-nio-3] o.s.c.sleuth.log.Slf4jScopeDecorator : With parent: 3276748429663156753
2020-06-19 13:52:15.526 INFO [FrontendUsingRestTemplate,2d795955f8643a11,79ec6c794e86cd58,false] 12564 --- [ctor-http-nio-3] FrontendUsingRestTemplate : Frontend RestTemplate::Sending request to Backend http://localhost:9000/api
2020-06-19 13:52:15.533 TRACE [FrontendUsingRestTemplate,2d795955f8643a11,79ec6c794e86cd58,false] 12564 --- [ctor-http-nio-3] s.n.www.protocol.http.HttpURLConnection : ProxySelector Request for http://localhost:9000/api
2020-06-19 13:52:15.535 TRACE [FrontendUsingRestTemplate,2d795955f8643a11,79ec6c794e86cd58,false] 12564 --- [ctor-http-nio-3] s.n.www.protocol.http.HttpURLConnection : Proxy used: DIRECT
2020-06-19 13:52:15.536 DEBUG [FrontendUsingRestTemplate,2d795955f8643a11,79ec6c794e86cd58,false] 12564 --- [ctor-http-nio-3] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader#161711d99 pairs: {GET /api HTTP/1.1: null}{Accept: text/plain, application/json, application/*+json, */*}{X-B3-TraceId: 2d795955f8643a11}{X-B3-SpanId: 79ec6c794e86cd58}{X-B3-ParentSpanId: 2d795955f8643a11}{X-B3-Sampled: 0}{User-Agent: Java/11.0.2}{Host: localhost:9000}{Connection: keep-alive}
2020-06-19 13:52:15.766 TRACE [FrontendUsingRestTemplate,2d795955f8643a11,79ec6c794e86cd58,false] 12564 --- [ctor-http-nio-3] s.n.www.protocol.http.HttpURLConnection : KeepAlive stream used: http://localhost:9000/api
2020-06-19 13:52:15.768 DEBUG [FrontendUsingRestTemplate,2d795955f8643a11,79ec6c794e86cd58,false] 12564 --- [ctor-http-nio-3] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader#1c1638c03 pairs: {null: HTTP/1.1 200 OK}{Content-Type: text/plain;charset=UTF-8}{Content-Length: 39}
2020-06-19 13:52:15.769 TRACE [FrontendUsingRestTemplate,2d795955f8643a11,79ec6c794e86cd58,false] 12564 --- [ctor-http-nio-3] o.s.c.sleuth.log.Slf4jScopeDecorator : Closing scope for span: 2d795955f8643a11/79ec6c794e86cd58
2020-06-19 13:52:15.771 DEBUG [FrontendUsingRestTemplate,2d795955f8643a11,2d795955f8643a11,false] 12564 --- [ctor-http-nio-3] o.s.web.client.RestTemplate : Response 200 OK
Looking for ideas on how to get span generated for Webclient calls into MDC for it to show up in the logs for request and response

why i get apikit:router not found 404 error in mule eventhough it requests an already running and deployed api

i have created 2 apis: system and experience for a project. The system had already been deployed into the cloudhub and running successfully. The experience api needs to invoke the system api through a router using the URL:
http://demo-insurance-system-api.us-e2.cloudhub.io
and uriparam is :customer
and queryparams are:?fname=James&lname=Butt
Its working perfectly fine.
but when i want to hit the same url from experience api's requester it gives me
ERROR 2020-05-18 01:58:15,217 [[muleinsurance-exp-api].http.requester.requestConfig.04 SelectorRunner] [event: ] org.mule.runtime.core.internal.exception.OnErrorPropagateHandler:
********************************************************************************
Message : HTTP OPTIONS on resource 'http://demo-insurance-system-api.us-e2.cloudhub.io' failed: not found (404).
Error type : HTTP:NOT_FOUND
Element : muleinsurance-experience-api-main/processors/0 # muleinsurance-exp-api:muleinsurance-experience-api.xml:17
Element XML : <apikit:router config-ref="muleinsurance-experience-api-config"></apikit:router>
(set debug level logging or '-Dmule.verbose.exceptions=true' for everything)
the onpremise testing is done in using postman and the experience url is:
http://localhost:8081/demoapi/customer?fname=James
the experience api raml is:
#%RAML 1.0
title: muleinsurance-experience-api
version: 1.0.0
traits:
client-id-required:
headers:
client_id:
type: string
client_secret:
type: string
responses:
401:
description: Unauthorized, The client_id or client_secret are not valid or the client does not have access.
404:
description: No Record found.
429:
description: The client used all of it's request quota for the current period.
500:
description: Server error ocurred
503:
description: Contracts Information Unreachable.
/demoapi:
/{searchString}:
get:
description: invokes either customer or policy request
queryParameters:
fname:
description: first name
example: Sumitra
required: false
type: string
lname:
description: Last name
example: Ojha
required: false
type: string
dob:
description: Date of Birth
example: 1/2/2003
required: false
type: string
customerID:
description: CustomerID
example: BU79786
required: false
type: string
policytype:
description: type of policy taken
example: Personal Auto
required: false
type: string
responses:
200:
body:
application/json:
example:
{"message": "connecting to System API"}
here i am adding the HTTP request xml snippet:
<choice doc:name="Choice" doc:id="444a5cd6-4aee-441a-8736-2d2fff681e2e" >
<when expression="#[attributes.uriParams.searchString == 'customer']">
<http:request method="GET" doc:name="Request" doc:id="30958d05-467a-41b1-bef3-83426359f2aa" url="http://demo-insurance-system-api.us-e2.cloudhub.io"><http:uri-params ><![CDATA[#[output application/java
---
{ customer : attributes.uriParams.searchString}]]]></http:uri-params>
<http:query-params ><![CDATA[#[output application/java
---
{ fname : vars.fname,
lname : vars.lname,
dob : vars.dob}]]]>
kindly point out where i need to improve. thanks in advance.
The problem is indicated in the error message: HTTP OPTIONS on resource 'http://demo-insurance-system-api.us-e2.cloudhub.io' failed: not found (404).
It looks like your HTTP Request in the experience API is using the HTTP Options method. The RAML indicates that the API only accepts the HTTP GET method. In the XML it should look like: <http:request method="GET" ...

How can I use location from Response Headers in the next request? [duplicate]

This question already has an answer here:
How to get id in responseHeaders location?
(1 answer)
Closed last year.
I'm perfoming some test using Karate test.
1st a Post that is expecting a 201 and a location.
2nd use the location from response to perfom a get.
Does anyone know how can I do this with Karate?
The following code is my try, after many other.
Given path 'alpha/test'
And request
"""
{
"id": '#(uuid)',
"content": "test",
"isActive": true
}
"""
When method post
Then status 201
And def endpointLocation = responseHeaders['Location']
And print endpointLocation
Given url 'endpointLocation'
When method get
Then status 200
And match response ==
"""
{
"id": '#(uuid)'
}
"""
But i'm getting:
18:30:12.819 [main] ERROR com.intuit.karate - org.apache.http.client.ClientProtocolException, http call failed after 252 milliseconds for URL: endpointLocation
18:30:12.820 [main] ERROR com.intuit.karate - http request failed:
org.apache.http.client.ClientProtocolException
testApi.feature:263 -
org.apache.http.client.ClientProtocolException
I'm not figuring it out why.
[EDIT]
After sugestion from Peter, that I Thanks!
The print return:
08:59:33.610 [main] INFO com.intuit.karate - [print] [
"https://www.test.com/enpoint/4603b043-ea8c-470d-a5a9-2aa50aea4f75"]
Now I'm calling
Given url endpointLocation
and I'm getting
08:59:33.611 [main] ERROR com.intuit.karate - http request failed: java.net.URISyntaxException: Illegal character in scheme name at index 0: ["https://www.test.com/enpoint/4603b043-ea8c-470d-a5a9-2aa50aea4f75"]
fraudMgmtApi.feature:263 - java.net.URISyntaxException: Illegal character in scheme name at index 0: ["https://www.test.com/enpoint/4603b043-ea8c-470d-a5a9-2aa50aea4f75"]
I was trying also to trim the first and last caracters, creating a:
def trim =
"""
function(myText) { result = myText.substring(1, myText.length-1) }
"""
Or myText.slice(1,-1)
From the error message: http call failed after 252 milliseconds for URL: endpointLocation
It is clear you are setting the url wrong. First try hard-coding it, then make this change, if the endpointLocation variable is set correctly. You can print it to check.
Given url endpointLocation

Is Spring webclient non-blocking client?

I don't understand reactive webclient works. It says that spring webclient is non-blocking client, but this webclient seems waiting signal onComplete() from remote api, then it can process each item that emitted from the remote api.
I'm expecting that webclient can process each item when onNext() is fired from the target api
I'm new in the spring webflux worlds. I read about it and it says it uses netty as default server. And this netty using eventloop. So to understand how it works I try to create 2 small apps, client and server.
Server app only return simple flux with delay 1 second each item.
Client app using webclient to call remote api.
Server:
#GetMapping(ITEM_END_POINT_V1)
public Flux<Item> getAllItems(){
return Flux.just(new Item(null, "Samsung TV", 399.99),
new Item(null, "LG TV", 329.99),
new Item(null, "Apple Watch", 349.99),
new Item("ABC", "Beats HeadPhones",
149.99)).delayElements(Duration.ofSeconds(1)).log("Item : ");
}
Client:
WebClient webClient = WebClient.create("http://localhost:8080");
#GetMapping("/client/retrieve")
public Flux<Item> getAllItemsUsingRetrieve() {
return webClient.get().uri("/v1/items")
.retrieve()
.bodyToFlux(Item.class).log();
}
Log from server:
2019-05-01 22:44:20.121 INFO 19644 --- [ctor-http-nio-2] Item : : onSubscribe(FluxConcatMap.ConcatMapImmediate)
2019-05-01 22:44:20.122 INFO 19644 --- [ctor-http-nio-2] Item : : request(unbounded)
2019-05-01 22:44:21.126 INFO 19644 --- [ parallel-1] Item : : onNext(Item(id=null, description=Samsung TV, price=399.99))
2019-05-01 22:44:22.129 INFO 19644 --- [ parallel-2] Item : : onNext(Item(id=null, description=LG TV, price=329.99))
2019-05-01 22:44:23.130 INFO 19644 --- [ parallel-3] Item : : onNext(Item(id=null, description=Apple Watch, price=349.99))
2019-05-01 22:44:24.131 INFO 19644 --- [ parallel-4] Item : : onNext(Item(id=ABC, description=Beats HeadPhones, price=149.99))
2019-05-01 22:44:24.132 INFO 19644 --- [ parallel-4] Item : : onComplete()
Log from client:
2019-05-01 22:44:19.934 INFO 24164 --- [ctor-http-nio-2] reactor.Flux.MonoFlatMapMany.1 : onSubscribe(MonoFlatMapMany.FlatMapManyMain)
2019-05-01 22:44:19.936 INFO 24164 --- [ctor-http-nio-2] reactor.Flux.MonoFlatMapMany.1 : request(unbounded)
2019-05-01 22:44:19.940 TRACE 24164 --- [ctor-http-nio-2] o.s.w.r.f.client.ExchangeFunctions : [7e73de5c] HTTP GET http://localhost:8080/v1/items, headers={}
2019-05-01 22:44:24.159 TRACE 24164 --- [ctor-http-nio-6] o.s.w.r.f.client.ExchangeFunctions : [7e73de5c] Response 200 OK, headers={masked}
2019-05-01 22:44:24.204 INFO 24164 --- [ctor-http-nio-6] reactor.Flux.MonoFlatMapMany.1 : onNext(Item(id=null, description=Samsung TV, price=399.99))
2019-05-01 22:44:24.204 INFO 24164 --- [ctor-http-nio-6] reactor.Flux.MonoFlatMapMany.1 : onNext(Item(id=null, description=LG TV, price=329.99))
2019-05-01 22:44:24.204 INFO 24164 --- [ctor-http-nio-6] reactor.Flux.MonoFlatMapMany.1 : onNext(Item(id=null, description=Apple Watch, price=349.99))
2019-05-01 22:44:24.204 INFO 24164 --- [ctor-http-nio-6] reactor.Flux.MonoFlatMapMany.1 : onNext(Item(id=ABC, description=Beats HeadPhones, price=149.99))
2019-05-01 22:44:24.205 INFO 24164 --- [ctor-http-nio-6] reactor.Flux.MonoFlatMapMany.1 : onComplete()
I'm expecting that client won't wait for 4 seconds then get the actual result.
As you can see that server start emit onNext() on 22:44:21.126, and client get result on 22:44:24.159.
So I don't understand why webclient is called non-blocking client if it has this behaviour.
The WebClient is non-blocking in a sense that the thread sending HTTP requests through the WebClient is not blocked by the IO operation.
When the response is available, netty will notify one of the worker threads and it will process the response according to the reactive stream operations that you defined.
In your example the server will wait until all the elements in a Flux are available (4 seconds), serialize them to the JSON array, and send it back in a single HTTP response.
The client waits for this single response, but non of its threads are blocked during this period.
If you want to achieve the streaming effect, you need to leverage different content-type or the underlying protocol like WebSockets.
Check-out the following SO thread about the application/stream+json content-type:
Spring WebFlux Flux behavior with non streaming application/json

Karate: How to test multipart form-data endpoint? [duplicate]

This question already has an answer here:
How to upload CSV file as a post request in Karate 0.9.0 version?
(1 answer)
Closed 2 years ago.
I have an file upload endpoint (/document) in a controller defined as follows:
#RestController
public class FileUploadController {
#Autowired
private PersonCSVReaderService personCSVReaderService;
#PostMapping(value = "/document", consumes= {MediaType.MULTIPART_FORM_DATA_VALUE})
public void handleFileUpload3(#RequestPart("file") MultipartFile file, #RequestPart("metadata") DocumentMetadata metadata) {
System.out.println(String.format("uploading file %s of %s bytes", file.getOriginalFilename(), file.getSize()));
personCSVReaderService.readPersonCSV(file, metadata);
}
}
I can test this endpoint using Advanced Rest Client (ARC) or Postman by defining the "file" part referencing the people.csv file and a text part specifying some sample metadata JSON.
Everything works fine and I get a 200 status back with the people.csv file contents being written to the console output by the service method:
uploading file people.csv of 256 bytes
{Address=1, City=2, Date of Birth=6, Name=0, Phone Number=5, State=3, Zipcode=4}
Person{name='John Brown', address='123 Main St.', city='Scottsdale', state='AZ', zipcode='85259', phoneNumber='555-1212', dateOfBirth='1965-01-01'}
Person{name='Jan Black', address='456 University Dr.', city='Atlanta', state='GA', zipcode='30306', phoneNumber='800-1111', dateOfBirth='1971-02-02'}
Person{name='Mary White', address='789 Possum Rd.', city='Nashville', state='TN', zipcode='37204', phoneNumber='888-2222', dateOfBirth='1980-03-03'}
Now, I want to run this as an automated Karate test. I have specified a MockConfig :
#Configuration
#EnableAutoConfiguration
#PropertySource("classpath:application.properties")
public class MockConfig {
// Services ...
#Bean
public PersonCSVReaderService personCSVReaderService() {
return new PersonCSVReaderService();
}
// Controllers ...
#Bean
public FileUploadController fileUploadController() {
return new FileUploadController();
}
}
I also have a MockSpringMvcServlet in the classpath and my karate-config.js is :
function fn() {
var env = karate.env; // get system property 'karate.env'
if (!env) {
env = 'dev';
}
karate.log('karate.env system property was:', env);
var config = {
env: env,
myVarName: 'someValue',
baseUrl: 'http://localhost:8080'
}
if (env == 'dev') {
var Factory = Java.type('MockSpringMvcServlet');
karate.configure('httpClientInstance', Factory.getMock());
//var result = karate.callSingle('classpath:demo/headers/common-noheaders.feature', config);
} else if (env == 'stg') {
// customize
} else if (env == 'prod') {
// customize
}
return config;
}
Other karate tests run ok using the mock servlet.
However, when I try this test to test the /document endpoint:
Feature: file upload end-point
Background:
* url baseUrl
* configure lowerCaseResponseHeaders = true
Scenario: upload file
Given path '/document'
And header Content-Type = 'multipart/form-data'
And multipart file file = { read: 'people.csv', filename: 'people.csv', contentType: 'text/csv' }
And multipart field metadata = { name: "joe", description: "stuff" }
When method post
Then status 200
I get this error:
16:14:42.674 [main] INFO com.intuit.karate - karate.env system property was: dev
16:14:42.718 [main] INFO o.s.mock.web.MockServletContext - Initializing Spring DispatcherServlet ''
16:14:42.719 [main] INFO o.s.web.servlet.DispatcherServlet - Initializing Servlet ''
16:14:43.668 [main] INFO o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$a4c7d08f] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
16:14:43.910 [main] INFO o.h.validator.internal.util.Version - HV000001: Hibernate Validator 6.0.14.Final
16:14:44.483 [main] INFO o.s.s.c.ThreadPoolTaskExecutor - Initializing ExecutorService 'applicationTaskExecutor'
16:14:44.968 [main] INFO o.s.b.a.e.web.EndpointLinksResolver - Exposing 2 endpoint(s) beneath base path '/actuator'
16:14:45.006 [main] INFO o.s.web.servlet.DispatcherServlet - Completed initialization in 2287 ms
16:14:45.066 [main] INFO c.i.k.mock.servlet.MockHttpClient - making mock http client request: POST - http://localhost:8080/document
16:14:45.085 [main] DEBUG c.i.k.mock.servlet.MockHttpClient -
1 > POST http://localhost:8080/document
1 > Content-Type: multipart/form-data
16:14:45.095 [main] ERROR com.intuit.karate - http request failed: null
file-upload.feature:13 - null
HTML report: (paste into browser to view) | Karate version: 0.9.2
I can only assume that the arguments did not conform to what my endpoint was expecting - I never entered the endpoint in debug mode.
I tried this:
And multipart file file = read('people.csv')
And multipart field metadata = { name: "joe", description: "stuff" }
But that was a non-starter as well.
What am I doing wrong? The people.csv is in the same folder as fileupload.feature, so I am assuming it is finding the file. I also looked at upload.feature file in the Karate demo project given here:
Karate demo project upload.feature
But I could not make it work. Any help appreciated. Thanks in advance.
The Postman request looks like this:
EDIT UPDATE:
I could not get the suggested answer to work.
Here is the feature file:
Feature: file upload
Background:
* url baseUrl
* configure lowerCaseResponseHeaders = true
Scenario: upload file
Given path '/document'
And header Content-Type = 'multipart/form-data'
* def temp = karate.readAsString('people.csv')
* print temp
And multipart file file = { value: '#(temp)', filename: 'people.csv', contentType: 'text/csv' }
And multipart field metadata = { value: {name: 'joe', description: 'stuff'}, contentType: 'application/json' }
When method post
Then status 200
And here is the console output from running that test:
09:27:22.051 [main] INFO com.intuit.karate - found scenario at line: 7 - ^upload file$
09:27:22.156 [main] INFO com.intuit.karate - karate.env system property was: dev
09:27:22.190 [main] INFO o.s.mock.web.MockServletContext - Initializing Spring DispatcherServlet ''
09:27:22.190 [main] INFO o.s.web.servlet.DispatcherServlet - Initializing Servlet ''
09:27:23.084 [main] INFO o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$a4c7d08f] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
09:27:23.327 [main] INFO o.h.validator.internal.util.Version - HV000001: Hibernate Validator 6.0.14.Final
09:27:23.896 [main] INFO o.s.s.c.ThreadPoolTaskExecutor - Initializing ExecutorService 'applicationTaskExecutor'
09:27:24.381 [main] INFO o.s.b.a.e.web.EndpointLinksResolver - Exposing 2 endpoint(s) beneath base path '/actuator'
09:27:24.418 [main] INFO o.s.web.servlet.DispatcherServlet - Completed initialization in 2228 ms
09:27:24.435 [main] INFO com.intuit.karate - [print] Name,Address,City,State,Zipcode,Phone Number,Date of Birth
John Brown,123 Main St.,Scottsdale,AZ,85259,555-1212,1965-01-01
Jan Black,456 University Dr.,Atlanta,GA,30306,800-1111,1971-02-02
Mary White,789 Possum Rd.,Nashville,TN,37204,888-2222,1980-03-03
09:27:24.482 [main] INFO c.i.k.mock.servlet.MockHttpClient - making mock http client request: POST - http://localhost:8080/document
09:27:24.500 [main] DEBUG c.i.k.mock.servlet.MockHttpClient -
1 > POST http://localhost:8080/document
1 > Content-Type: multipart/form-data
09:27:24.510 [main] ERROR com.intuit.karate - http request failed: null
file-upload.feature:14 - null
HTML report: (paste into browser to view) | Karate version: 0.9.2
Note: people.csv file reads successfully and prints in console.
Refer to this part of the docs: https://github.com/intuit/karate#read-file-as-string
So make this change:
* def temp = karate.readAsString('people.csv')
And multipart file file = { value: '#(temp)', filename: 'people.csv', contentType: 'text/csv' }
EDIT: my bad, also refer: https://github.com/intuit/karate#multipart-file
Feature: upload csv
Background: And def admin = read('classpath:com/project/data/adminLogin.json')
Scenario:
Given url baseUrl
And header Authorization = admin.token
And multipart file residentDetails = { read:'classpath:com/project/data/ResidentApp_Details.csv', filename: 'ResidentApp_Details.csv' }
When method POST
Then status 200
Note: Add only one extra line i.e And multipart file residentDetails = { read:'classpath:com/project/data/ResidentApp_Details.csv', filename: 'ResidentApp_Details.csv' }