I'm new to Groovy and a bit rusty on development.
I am trying to make a GET request to a woocommerce API. With Postman it works OK but not in Groovy it is not entirely clear to me what you need to cast from String to Map Entry.
I imagine it's something relatively simple but I can't find it.
Thanks a lot.
#Grab('com.github.groovy-wslite:groovy-wslite:1.1.2')
import groovy.json.JsonSlurper
import wslite.rest.*
def request
RESTClient client = new RESTClient("https://www.somewoomcersite.com")
def path = "/wp-json/wc/v3/customers"
request = client.get(path: path, headers: ["Authorization", "BLA BLA"])
Caught: java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Map$Entry
java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Map$Entry
at wslite.rest.RESTClient.createRequestParams(RESTClient.groovy:118)
at wslite.rest.RESTClient.this$2$createRequestParams(RESTClient.groovy)
at wslite.rest.RESTClient.executeMethod(RESTClient.groovy:93)
at wslite.rest.RESTClient.this$2$executeMethod(RESTClient.groovy)
at wslite.rest.RESTClient.get(RESTClient.groovy:49)
at wslite.rest.RESTClient.get(RESTClient.groovy)
at wslite.rest.RESTClient$get.call(Unknown Source)
at MAG.run(MAG.groovy:9)
An example json of the response would be this:
https://woocommerce.github.io/woocommerce-rest-api-docs/#list-all-customers
Related
I want to create a csv file and after it send it in the request. But, when I'm trying to do this, I'm getting an error:
class java.io.File cannot be cast to class java.lang.String (java.io.File and java.lang.String are in module java.base of loader 'bootstrap')
I'm creating file in the next way:
Via JS functions generating content of the file, and after it, I'm using karate.write:
* def csvFile = karate.write(csvContent,fileName)
But, when I'm trying to send this, it's an error appears:
And multipart file files = { read: '#(csvFile)', filename: 'bulkUpload.csv', contentType: 'multipart/form-data'}
When method POST
Then status 200
class java.io.File cannot be cast to class java.lang.String (java.io.File and java.lang.String are in module java.base of loader 'bootstrap')
What I'm doing wrong?
UPD: Also, I was trying to access created file directly, but it also doesn't work, even with absolute path
As mentioned in the docs, you get back a java.io.File instance: https://github.com/karatelabs/karate#karate-write
So do this:
* def csvFile = karate.write('foo', 'temp.txt')
* def csvPath = 'file:' + csvFile.getPath()
* multipart file files = { read: '#(csvPath)', filename: 'bulkUpload.csv', contentType: 'multipart/form-data' }
Note the use of .txt because of this: https://stackoverflow.com/a/55643097/143475
That said, I please read this before proceeding down this path: https://stackoverflow.com/a/54593057/143475
I have succesfully written a simple use-case to unmarshal a JSON response from the Quickbooks oAuth API to refresh tokens.
With that working, I need to go and fetch the actual data:
https://quickbooks.api.intuit.com/v3/company/XXXXXXXXXXXXXX/query?query=SELECT * FROM Invoice WHERE Metadata.LastUpdatedTime%3E='2020-07-01T01:00:00' ORDERBY Metadata.LastUpdatedTime, Id STARTPOSITION 1 MAXRESULTS 1000 &minorversion=47
The HTTP call works OK:
// make the HTTP REST call, without C10y* & Camel* headers:
.toD("https://${header." + Headers.IEP_API_HOST + "}?headerFilterStrategy=C10yHeaderFilterStrategy")
I can check that the JSON returned is OK:
.log(LoggingLevel.DEBUG, API_LOG, "JSON returned: ${body}")
But from here it goes pear-shaped:
.unmarshal().json(JsonLibrary.Jackson, Payload.class)
This is what happens:
With the above log statement a MismatchedInputException is raised with the message "No content to map due to end-of-input".
Without the above log statement a ClassNotFoundException is raised with the message "Payload.class".
Re. 1.
My understanding is that responses are cached by default so it should be possible to read the inputstream multiple times. By default the HTTP endpoint option disableStreamCache is set to false, so it's enabled.
The stack trace is:
com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
at [Source: (org.apache.camel.converter.stream.CachedOutputStream$WrappedInputStream); line: 1, column: 0]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4624) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4469) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3471) ~[jackson-databind-2.11.0.jar:2.11.0]
at org.apache.camel.component.jackson.JacksonDataFormat.unmarshal(JacksonDataFormat.java:188) ~[camel-jackson-3.4.0.jar:3.4.0]
at org.apache.camel.support.processor.UnmarshalProcessor.process(UnmarshalProcessor.java:64) ~[camel-support-3.4.0.jar:3.4.0]
at org.apache.camel.processor.errorhandler.RedeliveryErrorHandler$RedeliveryTask.doRun(RedeliveryErrorHandler.java:702) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.processor.errorhandler.RedeliveryErrorHandler$RedeliveryTask.run(RedeliveryErrorHandler.java:616) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.impl.engine.DefaultReactiveExecutor$Worker.schedule(DefaultReactiveExecutor.java:148) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.impl.engine.DefaultReactiveExecutor.scheduleMain(DefaultReactiveExecutor.java:60) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.processor.Pipeline.process(Pipeline.java:147) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:286) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.component.timer.TimerConsumer.sendTimerExchange(TimerConsumer.java:203) ~[camel-timer-3.4.0.jar:3.4.0]
at org.apache.camel.component.timer.TimerConsumer$1.run(TimerConsumer.java:76) ~[camel-timer-3.4.0.jar:3.4.0]
at java.base/java.util.TimerThread.mainLoop(Timer.java:556) ~[na:na]
at java.base/java.util.TimerThread.run(Timer.java:506) ~[na:na]
Re. 2.
The stack trace for this is:
org.apache.camel.CamelExecutionException: Exception occurred during execution on the exchange: Exchange[ID-WIN-10-DM-1594724136485-0-1]
at org.apache.camel.CamelExecutionException.wrapCamelExecutionException(CamelExecutionException.java:47) ~[camel-api-3.4.0.jar:3.4.0]
at org.apache.camel.language.simple.SimpleExpressionBuilder$26.evaluate(SimpleExpressionBuilder.java:590) ~[camel-core-languages-3.4.0.jar:3.4.0]
at org.apache.camel.support.ExpressionAdapter.evaluate(ExpressionAdapter.java:36) ~[camel-support-3.4.0.jar:3.4.0]
at org.apache.camel.reifier.language.SimpleExpressionReifier$1.evaluate(SimpleExpressionReifier.java:42) ~[camel-core-engine-3.4.0.jar:3.4.0]
at org.apache.camel.processor.SetHeaderProcessor.process(SetHeaderProcessor.java:48) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.processor.errorhandler.RedeliveryErrorHandler$RedeliveryTask.doRun(RedeliveryErrorHandler.java:702) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.processor.errorhandler.RedeliveryErrorHandler$RedeliveryTask.run(RedeliveryErrorHandler.java:616) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.impl.engine.DefaultReactiveExecutor$Worker.schedule(DefaultReactiveExecutor.java:148) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.impl.engine.DefaultReactiveExecutor.scheduleMain(DefaultReactiveExecutor.java:60) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.processor.Pipeline.process(Pipeline.java:147) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:286) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.component.timer.TimerConsumer.sendTimerExchange(TimerConsumer.java:203) ~[camel-timer-3.4.0.jar:3.4.0]
at org.apache.camel.component.timer.TimerConsumer$1.run(TimerConsumer.java:76) ~[camel-timer-3.4.0.jar:3.4.0]
at java.base/java.util.TimerThread.mainLoop(Timer.java:556) ~[na:na]
at java.base/java.util.TimerThread.run(Timer.java:506) ~[na:na]
Caused by: java.lang.ClassNotFoundException: Payload.class
at org.apache.camel.impl.engine.DefaultClassResolver.resolveMandatoryClass(DefaultClassResolver.java:87) ~[camel-base-3.4.0.jar:3.4.0]
at org.apache.camel.language.simple.SimpleExpressionBuilder$26.evaluate(SimpleExpressionBuilder.java:588) ~[camel-core-languages-3.4.0.jar:3.4.0]
... 13 common frames omitted
All of this is running within an Eclipse/Maven project, so I have cleaned, refreshed, compiled, rebuilt, etc, etc, to no avail.
I have written a simple jUnit and it works absolutely fine using the JSON from the above log, saved to a file:
/**
* POJO Jackson unmarshalling
*/
#Test
public void pojoUnmarshallTest() {
ObjectMapper om = new ObjectMapper();
try {
Payload payload = om.readValue(getFile("qb.Payload.Invoice.json"), Payload.class);
assertTrue(payload.toString().startsWith("c10y.model.qb.Payload"));
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
Lastly, I'm using the latest versions of Camel & Spring Boot:
<properties>
<!-- latest versions # Jul 2020 -->
<java.version>11</java.version>
<camel.version>3.4.0</camel.version> <!-- latest long term support version -->
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
<spring-boot.version>2.3.0.RELEASE</spring-boot.version>
<run.profiles>dev</run.profiles>
</properties>
As ever, thanks in advance for your help!
PS:
The POJO classes that receive the unmarshalled JSON were generated at http://www.jsonschema2pojo.org/. The POJO for the working route was hand-cranked. I mention it just in case it could make a difference (it shouldn't IMHO).
The entire route can be obtained here: https://drive.google.com/file/d/1Qu0vwJaSlggH6BrIgUFMU_BZNpmMQYuh/view?usp=sharing.
I have tried the Gson library and get the same ClassNotFoundException: Payload.class error. This is looking like less of an unmarshalling problem and more of a classpath issue.
Having two .log() statements gives the following output:
2020-07-15 10:06:27.108 DEBUG 6720 --- [mer://startHere] c.c.r.qb.fetch.delta : JSON returned: {"QueryResponse":{"Invoice":[{"AllowIPNPayment":false, ...removed... ,"Balance":0}],"startPosition":1,"maxResults":1,"totalCount":1},"time":"2020-07-15T02:06:21.869-07:00"}
2020-07-15 10:06:27.108 DEBUG 6720 --- [mer://startHere] c.c.r.qb.fetch.delta : JSON returned:
Contrary to the documentation, it looks like the input stream is not, in fact, cached.
PS2:
Adding .streamCaching() to the route and &disableStreamCache=false to the endpoint URI didn't make any difference to the second .log(); it remained empty.
I also tried the following Java Config approach:
#Configuration
public class Config {
#Bean
CamelContextConfiguration contextConfiguration() {
return new CamelContextConfiguration() {
#Override
public void beforeApplicationStart(CamelContext context) {
System.out.println("****** beforeApplicationStart ******");
}
#Override
public void afterApplicationStart(CamelContext context) {
System.out.println("****** afterApplicationStart ******");
context.setStreamCaching(true);
}
};
}
}
I can see the sysout in the console but this didn't work either.
I downloaded jsonschema2pojo-1.0.2 and ran it against a much bigger JSON sample with the following arguments:
--annotation-style JACKSON2
--big-decimals
--date-class java.util.Date
--format-dates
--format-date-times
--package c10y.model.qb.jxon
--remove-old-output
--source C:\Users\...\src\test\resources\qb.Payload.Invoice.json
--source-type JSON
--target c:\temp
--target-language JAVA
--target-version 11
This created the root/base POJO called QbPayloadInvoice, which looks like it's taken from the input file name. I updated my route:
.unmarshal().json(JsonLibrary.Jackson, QbPayloadInvoice.class)
It still raises the java.lang.ClassNotFoundException: Payload.class.
There's nothing in the JSON response, or in any of the other generated POJOs, called Payload.
At the same time, my updated jUnit works fine:
QbPayloadInvoice payload = om.readValue(getFile("qb.Payload.Invoice.json"), QbPayloadInvoice.class);
expected = "c10y.model.qb.jxon.QbPayloadInvoice";
assertEquals(expected, payload.toString().substring(0, expected.length()));
Go figure!
The clue exists above where I wrote "There's nothing in the JSON response, or in any of the other generated POJOs, called Payload." This statement turned out to be absolutely correct.
The unmarshalling had been working all along:
.unmarshal().json(JsonLibrary.Jackson, QbPayloadInvoice.class)
It was the following line that was failing:
.setHeader(QB_START_POS, simple("${bodyAs(Payload.class).QueryResponse.startPosition}"))
This was made more obvious given that I'd changed the unmarshalling class from Payload to QbPayloadInvoice.
I'm trying to create a REST API from a SOAP Service using IBM API Connect 5. I have followed all the steps described in this guide (https://www.ibm.com/support/knowledgecenter/en/SSFS6T/com.ibm.apic.apionprem.doc/tutorial_apionprem_expose_SOAP.html).
So, after dragging the web service block from palette, ensuring the correctness of endpoint and publishing the API, I have tried to call the API from the browser. Unfortunately, the API return the following message:
<errorResponse>
<httpCode>500</httpCode>
<httpMessage>Internal Server Error</httpMessage>
<moreInformation>Error attempting to read the urlopen response
data</moreInformation>
</errorResponse>
To testing purpose, I have logged the request and I have tried the request on SOAPUI. The service return the response correctly.
What is the problem?
In my case, the problem was in the backend charset (Content-Type: text/xml;charset=iso-8859-1).
For example, backend returns text/xml in German (or French). Api Connect cannot process character ΓΌ. It needs Content-Type: text/xml;charset=UTF-8.
I had a similar issue, in my case was the accept. if you have an Invoke and the content-type or the accept, is not matching the one of the request, or the response that you got, APIC is getting mad.
Please, check if the formats to send (contentType) and receive (accept) are the same of that your API expected. In my case the error occurs because the API returns a String and my default code is configured to receive a JSON body.
//define a JSON-PLAIN TEXT protocol
private HttpEntity<String> httpEntityWithBody(Object objToParse){
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + "xxx token xxx");
headers.set("Accept", MediaType.TEXT_PLAIN_VALUE);
headers.setContentType(MediaType.APPLICATION_JSON);
Gson gson = new GsonBuilder().create();
String json = gson.toJson(objToParse);
HttpEntity<String> httpEntity = new HttpEntity<String>(json, headers);
return httpEntity;
}
//calling the API to APIC...
ParameterizedTypeReference<String> responseType = new
ParameterizedTypeReference<String>(){};
ResponseEntity<String> result =
rest.exchange(builder.buildAndExpand(urlParams).toUri(), HttpMethod.PUT, httpEntityWithBody(myDTO), responseType);
String statusCode = result.getStatusCodeValue();
String message = result.getBody();
I have seen in tutorial (Jersey) that there is possibility to pass in JAX-RS #PathParam of type Long.
But when I have tried to do this with RESTEasy I'm getting error:
21:50:32,353 WARN [org.jboss.resteasy.core.ExceptionHandler] (default task-15) failed to execute: javax.ws.rs.NotSupportedException: Could not find message body reader for type: long of content type: */*
at org.jboss.resteasy.core.interception.ServerReaderInterceptorContext.throwReaderNotFound(ServerReaderInterceptorContext.java:52)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.getReader(AbstractReaderInterceptorContext.java:73)
I tested it with Integer, but it also don't work...
22:14:45,590 WARN [org.jboss.resteasy.core.ExceptionHandler] (default task-18) failed to execute: javax.ws.rs.NotSupportedException: Could not find message body reader for type: class java.lang.Integer of content type: */*
at org.jboss.resteasy.core.interception.ServerReaderInterceptorContext.throwReaderNotFound(ServerReaderInterceptorContext.java:52)
Moreover I am trying to take this ID value as String and then convert it to Long using Long.valueOf(String s) or Long.parseLong(String s) and in both cases I am getting error
Caused by: java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:601)
at java.lang.Long.parseLong(Long.java:631)
at pl.salonea.jaxrs.UserAccountResource.getUserAccount(UserAccountResource.java:41)
It's the code of my web service resource method:
#GET
#Path("/{userId}")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public UserAccount getUserAccount(#PathParam("userId") String userId) {
return userAccountFacade.find(Long.parseLong(userId));
}
SOLVED
I have bad import not import javax.ws.rs.PathParam;, but something from web sockets PathParam... I just relay on IntelliJ automatic imports and haven't seen that it was wrong.
I have a REST request that respond with the following:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlQwWE8xNnAtMmZzMWxremV5",
"expires_in": 2592000,
"token_type": "Bearer"
}
I want to take the value of access_token, store it in a property and reuse it for two subsequent requests.
Following some tutorial here, when running the request that obtains the access_token I get a:
error parsing target property: error unexpected element CDATA
But why?
There is no CDATA in my raw response.
If you've problems using transfer properties step to get the JSON value from your response, you can use a groovy test step to achieve your goal.
So create a groovy test step to parse your response, get your value and set it as a property (for example at testCase level) with the follow code:
import groovy.json.JsonSlurper
// get response using the name of your test step
def response = context.expand('${REST Test Request#Response}')
// parse response
def jsonResp = new JsonSlurper().parseText(response)
// get the token an set as a property in the testCase
testRunner.testCase.setPropertyValue('access_token',jsonResp.access_token)
Then in the other testSteps (REST or SOAP...) you can use the follow code to get the access_token value you set in the testCase:
${#TestCase#access_token}
Hope this helps,