I am currently consuming content in the style of
"potter",["potter harry","potter","potter hallows 2","potter collection","potter hallows 1","potter dvd box","potter 7","potter hallows","potter blue ray","potter feniks"],[{"xcats":[{"name":"dvd_all"},{"name":"books_nl"}]},{},{},{},{},{},{},{},{},{}],[]]
Using the following code in Spring
RestTemplate restTemplate = new RestTemplate();
String information = restTemplate.getForObject(URL, String.class);
//further parsing of information, using String utilities
Obviously this is not the way to go, because I should be able to automatically parse it somehow. I will also only need the content of the second element as well (the array, from potter harry to potter feniks).
What is the best way to parse a GET response like that, when its json contents aren't name-valued?
#Test
public void testJson(){
RestTemplate template = new RestTemplate();
ResponseEntity<ArrayNode> entity = template.
exchange("https://api.myjson.com/bins/2rl7m", HttpMethod.GET, null, new ParameterizedTypeReference<ArrayNode>() {
});
ArrayNode body = entity.getBody();
body.get(1).forEach(m->{
System.out.println(m.asText());});
}
But my advice is if you can change the response type not to be json array with mixed value types in it will be better
The following helper class enabled me to easily parse the response and get the list I needed
public class JSONHelper {
private static final JsonFactory factory = new JsonFactory();
private static final ObjectMapper mapper = new ObjectMapper(factory);
public static List<String> getListOnPosition(int i, String inputWithFullList) throws JsonProcessingException, IOException {
List<String> result = new ArrayList<String>();
JsonNode rootNode = mapper.readTree(inputWithFullList);
ArrayNode node = (ArrayNode) rootNode.get(i);
if (!node.isArray()) {
result.add(node.asText());
} else {
for (final JsonNode subNode : node) {
result.add(subNode.asText());
}
}
return result;
}
}
Some JUnit tests for this scenario
public class JSONHelperTest {
#Test
public void parseListOnPositionFullListTest() throws JsonProcessingException, IOException {
String inputWithFullList = "[\"a\",[\"b\", \"c\", \"d\"],[],[]]";
List<String> result = JSONHelper.getListOnPosition(1, inputWithFullList);
assertEquals(3, result.size());
assertEquals(Arrays.asList("b", "c", "d"), result);
}
#Test
public void parseListOnPositionOneElementListTest() throws JsonProcessingException, IOException {
String inputWithFullList = "[\"a\",[\"b\"],[],[]]";
List<String> result = JSONHelper.getListOnPosition(1, inputWithFullList);
assertEquals(1, result.size());
assertEquals(Arrays.asList("b"), result);
}
#Test
public void parseListOnPositionEmptyListTest() throws JsonProcessingException, IOException {
String inputWithFullList = "[\"a\",[],[],[]]";
assertTrue(JSONHelper.getListOnPosition(1, inputWithFullList).isEmpty());
}
}
Related
This is a project I'm working on to basically get info on certain stocks.
The original working code is:
public static CommandLineRunner run(RestTemplate restTemplate) throws Exception {
return args -> {
Data data = restTemplate.getForObject(
"https://finnhub.io/api/v1/stock/insider-transactions?symbol=CRM&token=c683tuqad3iagio36uj0", Data.class);
log.info(data.toString());
System.out.println(data);
ResponseEntity<List<String>> peersResponse =
restTemplate.exchange("https://finnhub.io/api/v1/stock/peers?symbol=CRM&token=c683tuqad3iagio36uj0",
HttpMethod.GET, null, new ParameterizedTypeReference<List<String>>() {
});
List<String> peers = peersResponse.getBody();
System.out.println(peers);
};
}
And what I'm trying to do is to add a user "input" to add in between the URL to get info on a stock.
There are several issues at hand here:
I know CommandLineRunner starts right away when you run the app. What is a good way to make my app wait for an "input" from the user?
And secondly, to use that "input" within the API url to request the data?
I am not sure if I am explaining myself correctly and I hope by showing my original code and the code that I want to achieve will make it easier for any experts out there to understand. Thanks for any help!
What I want to do:
public static CommandLineRunner run(RestTemplate restTemplate) throws Exception {
return args -> {
#SuppressWarnings("resource")
Scanner input = new Scanner(System.in);
Data data = restTemplate.getForObject(
"https://finnhub.io/api/v1/stock/insider-transactions?symbol=" + input + "&token=c683tuqad3iagio36uj0", Data.class);
log.info(data.toString());
System.out.println(data);
ResponseEntity<List<String>> peersResponse =
restTemplate.exchange("https://finnhub.io/api/v1/stock/peers?symbol=" + input + "&token=c683tuqad3iagio36uj0",
HttpMethod.GET, null, new ParameterizedTypeReference<List<String>>() {
});
List<String> peers = peersResponse.getBody();
System.out.println(peers);
};
}
I just modified my code to correctly accept "input" to the API URL, as well as showing more of my codes:
public class StocksQueryApplication {
private static final Logger log = LoggerFactory.getLogger(StocksQueryApplication.class);
private static Scanner input = new Scanner(System.in);
public static void main(String[] args) {
SpringApplication.run(StocksQueryApplication.class, args);
}
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
#Bean
public static CommandLineRunner run(RestTemplate restTemplate) throws Exception {
return args -> {
Data data = restTemplate.getForObject(
"https://finnhub.io/api/v1/stock/insider-transactions?symbol=" + input + "&token=c683tuqad3iagio36uj0", Data.class);
log.info(data.toString());
System.out.println(data);
ResponseEntity<List<String>> peersResponse =
restTemplate.exchange("https://finnhub.io/api/v1/stock/peers?symbol=" + input + "&token=c683tuqad3iagio36uj0",
HttpMethod.GET, null, new ParameterizedTypeReference<List<String>>() {
});
List<String> peers = peersResponse.getBody();
System.out.println(peers);
};
}
}
What's left is just to make CommandLineRunner wait for a user "input" then run. But I can't seem to figure out how to do that.
i have this lines in my code:
#Autowired
private ObjectMapper mapper = new ObjectMapper();
#PostMapping("/postPrueba")
public ResponseEntity<String> postPrueba(#RequestBody Prueba prueba) {
String pTest = null;
try {
pTest = mapper.writeValueAsString(prueba);
System.out.println(pTest );
} catch (JsonProcessingException e) {
return new ResponseEntity<>("", HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>("", HttpStatus.OK);
}
My model Prueba.java
public class Prueba {
#Id
private String nombre;
private String apellidos;
private String edad;
}
And in test i want to force the JsonProcessingException but i canĀ“t. I already try this:
#Mock
private ObjectMapper mapperMock;
#Test
public void testKo() throws Exception {
ObjectMapper om = Mockito.spy(new ObjectMapper());
Mockito.when(this.mapperMock.writeValueAsString(Mockito.any())).thenThrow(new JsonProcessingException("") {});
ObjectMapper om = Mockito.spy(new ObjectMapper());
Mockito.when( om.writeValueAsString(Mockito.eq(Prueba.class))).thenThrow(new JsonProcessingException("") {});
Mockito.when(this.mapperMock.writeValueAsString(Mockito.eq(Prueba.class))).thenThrow(new JsonProcessingException("") {});
String jsonContent = "{'nombre': '123456', 'apellidos': '12'}";
jsonContent = jsonContent.replaceAll("\'", "\"");
this.mvc.perform(post("/postPrueba")
.contentType(MediaType.APPLICATION_JSON)
.content(jsonContent))
.andExpect(status().is5xxServerError());
}
But always the response is a 200 OK. How can i do this?
doThrow(JsonProcessingException.class).when(object).method(...);
You'll need to mock your http behaviour because the ObjectMapper is out of the scope. Found WireMock fits your purpose in terms of filtering http traffic and mocking responses
Rather then throwing JsonProcessingException from test just make ObjectMapper actually throw that exception with some false data to process.
You need to replace the Bean in the spring context.
I assume you use Spring Boot with #RunWith(SpringRunner.class) and #WebMVCTest?
Then you can do this using #MockBean
See also: https://www.baeldung.com/java-spring-mockito-mock-mockbean
Try this:
Mockito.doThrow(JsonProcessingException.class).when(this.mapperMock).writeValueAsString(Mockito.any());
So I have a json file that has some request and response data, and what I want to accomplish is iterate through this data and create a pact file that uses each request and response.
So at the moment I am using a parameterized test in junit to kinda iterate through our json data, and this basically works except for because the producer name is the same for all pacts, it creates the same file and is overwriting the previous.
private JsonObject requestObject;
private static Gson gson = new Gson();
private static File jsonFile = readJsonFile();
private static int randValue = new Random().nextInt(500);
private static String consmerName = "phx-ev-consumer" + randValue;
#Rule
public PactProviderRuleMk2 provider = new PactProviderRuleMk2("phx-ev-svc-provider", "localhost", 8080, this);
final RestTemplate restTemplate = new RestTemplate();
public EligibilityApiConsumerPactTest(JsonObject requestObject) {
this.requestObject = requestObject;
}
#Parameterized.Parameters
public static Collection primeNumbers() throws JsonSyntaxException, JsonIOException, FileNotFoundException {
return getJson();
}
#Pact(state = "provider accets submit contact form", provider = "phx-ev-svc-provider" , consumer = "phx-ev-consumer")
public RequestResponsePact createFragment(PactDslWithProvider builder) {
Map<String, String> requestHeaders = new HashMap<>();
requestHeaders.put("Content-Type", "application/json");
requestHeaders.put("SM_USER", "wtadmin");
requestHeaders.put("Cookie", "SMCHALLENGE=YES");
// Auth headers
String authString = "wtadmin:labcorp1";
String authEncoded = Base64.getEncoder().encodeToString(authString.getBytes());
requestHeaders.put("Authorization", "Basic " + authEncoded);
Map<String, String> responseHeaders = new HashMap<>();
responseHeaders.put("Content-Type", "application/json");
String jsonRequest = requestObject.get("request").toString();
String jsonResponse = requestObject.get("response").toString();
RequestResponsePact pact = builder.given("phx-eligibility").uponReceiving("Phoenix Eligibility Request")
.method("POST").headers(requestHeaders).body(jsonRequest).path("/phx-rest/eligibility")
.willRespondWith().status(200).headers(responseHeaders).body(jsonResponse).toPact();
return pact;
}
#Test
#PactVerification("phx-ev-svc-provider")
public void runTest() throws IOException {
MultiValueMap<String, String> requestHeaders = new LinkedMultiValueMap<>();
requestHeaders.add("Content-Type", "application/json");
requestHeaders.add("SM_USER", "wtadmin");
requestHeaders.add("Cookie", "SMCHALLENGE=YES");
// Auth headers
String authString = "wtadmin:labcorp1";
String authEncoded = Base64.getEncoder().encodeToString(authString.getBytes());
requestHeaders.add("Authorization", "Basic " + authEncoded);
String jsonRequest = requestObject.get("request").toString();
restTemplate.exchange(provider.getConfig().url() + "/phx-rest/eligibility", HttpMethod.POST,
new HttpEntity<>(jsonRequest, requestHeaders), String.class);
}
public static List<JsonObject> getJson() throws JsonSyntaxException, JsonIOException, FileNotFoundException {
List<JsonObject> results = new ArrayList<JsonObject>();
JsonObject jsonObject = gson.fromJson(new FileReader(jsonFile), JsonObject.class);
JsonArray input = jsonObject.getAsJsonArray("input");
Iterator<JsonElement> iter = input.iterator();
while (iter.hasNext()) {
JsonObject obj = (JsonObject) iter.next();
results.add(obj);
}
return results;
}
public static File readJsonFile() {
File base = new File("");
File inputFile = new File(base.getAbsolutePath() + "/pact/input/eligibility.json");
return inputFile;
}
Not sure if there is a better way to accomplish this, I have looked at the Github for Pact Jvm and looked through stack overflow but have not been able to find someone creating pact files, without statically specifying all of the data.
A Pact file is essentially a JSON document that contains details about a consumer, a provider and a list of interactions. In your case, you seems to have the same consumer and provider, but a JSON file with the requests and responses that make up the interactions.
So you need to create a single pact file, but with an interaction added for each item in your JSON file.
There are a number of ways you can do that, but if you modify your example test, you can chain the calls using the DSL builder by calling .uponReceiving again after the last .body. You can do this in a loop, each additional call to .uponReceiving will start adding a new interaction to the pact. You will have to give each interaction a unique description.
Then call .toPact() at the end to create the final pact.
Here is my endpoint :
#PostMapping("/email/registration")
public Data registration(#RequestBody Data data, HttpServletRequest
httpServletRequest) throws Exception {
emailSenderService.pushEmail(httpServletRequest.getRequestURI(),
emailSenderService.sendConfirmationEmail(httpServletRequest.getRequestURI(), data));
emailSenderService.consumeEmail(httpServletRequest.getRequestURI());
return data;
}
And here is my test(of course it's not working) :
#Test
public void testRegistrationConfirmation() throws Exception {
Data testData = new Data();
when(testData).thenReturn(testData);
mockMvc.perform(
post("/email/registration")
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(testData)))
.andExpect(status().isOk())
.andExpect(testData);
}
I would like to see if the give object is the same as the return object, preferably without filling all the fields with values.
Thank you for the answers!
It could be more elegant but you can use Jackson to make the json for you:
#Test
public void testRegistrationConfirmation() throws Exception {
Data testData = new Data();
when(testData).thenReturn(testData);
mockMvc.perform(
post("/email/registration")
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(testData)))
.andExpect(status().isOk())
.andExpect(content().string(equalTo(new ObjectMapper().writeValueAsString(testData))));
}
I am using Google Http Client library (1.20) on Google App Engine (1.9.30) to submit a POST request to Google Cloud Messaging (GCM) servers. Here's the code:
public static HttpRequestFactory getGcmRequestFactory() {
if (null == gcmFactory) {
gcmFactory = (new UrlFetchTransport())
.createRequestFactory(new HttpRequestInitializer() {
#Override
public void initialize(HttpRequest request) throws IOException {
request.getHeaders().setAuthorization(
"key=" + Config.get(Config.Keys.GCM_SERVER_API_KEY).orNull());
request.getHeaders().setContentType("application/json");
request.getHeaders().setAcceptEncoding(null);
}
});
}
return gcmFactory;
}
public static JsonFactory getJsonFactory() {
return jacksonFactory;
}
public static String sendGcmMessage(GcmDownstreamDto message) {
HttpRequestFactory factory = getGcmRequestFactory();
JsonHttpContent content = new JsonHttpContent(getJsonFactory(), message);
String response = EMPTY;
try {
HttpRequest req = factory.buildPostRequest(gcmDownstreamUrl, content);
log.info("req headers = " + req.getHeaders());
System.out.print("req content = ");
content.writeTo(System.out); // prints out "{}"
System.out.println(EMPTY);
HttpResponse res = req.execute(); // IOException here
response = IOUtils.toString(res.getContent());
} catch (IOException e) {
log.log(Level.WARNING, "IOException...", e);
}
return response;
}
Now the content.writeTo() always prints out empty JSON. Why is that? What am I doing wrong? The GcmDownstreamDto class (using Lombok to generate getters and setters):
#Data
#Accessors(chain = true)
public class GcmDownstreamDto {
private String to;
private Object data;
private List<String> registration_ids;
private GcmNotificationDto notification;
public GcmDownstreamDto addRegistrationId(String regId) {
if (null == this.registration_ids) {
this.registration_ids = new ArrayList<>();
}
if (isNotBlank(regId)) {
this.registration_ids.add(regId);
}
return this;
}
}
The immediate goal is to generate the same response as (from Checking the validity of an API key):
api_key=YOUR_API_KEY
curl --header "Authorization: key=$api_key" \
--header Content-Type:"application/json" \
https://gcm-http.googleapis.com/gcm/send \
-d "{\"registration_ids\":[\"ABC\"]}"
{"multicast_id":6782339717028231855,"success":0,"failure":1,
"canonical_ids":0,"results":[{"error":"InvalidRegistration"}]}
I've already tested using curl so I know the API key is valid, I just want to do the same thing in Java code to build up my base classes.
sendGcmMessage() is being invoked as follows:
#Test
public void testGcmDownstreamMessage() {
GcmDownstreamDto message = new GcmDownstreamDto().addRegistrationId("ABC");
System.out.println("message = " + message);
String response = NetCall.sendGcmMessage(message);
System.out.println("Response: " + response);
}
All help appreciated.
Found out the problem: it's the way JacksonFactory().createJsonGenerator().searialize() works (I was expecting it to serialize the way ObjectMapper serializes). This is the code for JsonHttpContent.writeTo() (from JsonHttpContent.java in google-http-java-client):
public void writeTo(OutputStream out) throws IOException {
JsonGenerator generator = jsonFactory.createJsonGenerator(out, getCharset());
generator.serialize(data);
generator.flush();
}
The Jackson JsonGenerator expects a key-value pairing (represented in Java as Map) which is not obvious from the constructor signature of the JsonHttpContent constructor: JsonHttpContent(JsonFactory, Object).
So if instead of passing a GcmDownstreamDto (as defined in the question, which is what would have worked with an ObjectMapper), I were to do the following:
Map<String, Object> map = new HashMap<>();
List<String> idList = Arrays.asList("ABC");
map.put("registration_ids", idList);
everything works as expected and the output is:
{"registration_ids":["ABC"]}
So just remember to pass the JsonHttpContent(JsonFactory, Object) constructor a Map<String, Object> as the second parameter, and everything will work as you would expect it to.
You need to annotate the POJO fields with #Key:
import com.google.api.client.util.Key;
// ...
#Key private String to;
#Key private Object data;
#Key private List<String> registration_ids;
// ...