Helo guys, sorry for my English =(
I am building a consumer of the jira api and I had the following error "Can not deserialize instance of Jira out of START_ARRAY token"
My JSON looks like this:
[
{
"expand": "description,lead,url,projectKeys",
"self": "http://",
"id": "10802",
"key": "TE",
"name": "TEST TEST",
"avatarUrls": {
"48x48": "http://",
"24x24": "http://",
"16x16": "http://",
"32x32": "http://"
},
"projectCategory": {
"self": "http://",
"id": "10200",
"name": "TTTTTT",
"description": "TTTTTTTT"
},
"projectTypeKey": "software"
},
{
"expand": "description,lead,url,projectKeys",
"self": "http://",
"id": "10801",
"key": "TT",
"name": "TREINAMENTO TESTE",
"avatarUrls": {
"48x48": "http://",
"24x24": "http://",
"16x16": "http://",
"32x32": "http://"
},
"projectTypeKey": "business"
}
]
Here's my code.
public class Project {
private String expand;
private String self;
private int ID;
private String key;
private String name;
private Avatar avatarUrls;
private ProjectCategory projectCategory;
private String projectTypeKey;
//get and setter
}
public class Jira {
private ArrayList<Project> projects;
public Jira() {
}
public ArrayList<Project> getProjects() {
return projects;
}
public void setProjects(ArrayList<Project> projects) {
this.projects = projects;
}
}
public class Application {
public static void main(String args[]) throws IOException {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders header = new HttpHeaders();
//AUTHORIZATION CIT
header.set("Authorization", "Basic XXXXXXXXX");
header.set("app_token", "XXXXXXXXX");
HttpEntity entity = new HttpEntity(header);
ResponseEntity <Jira> result = restTemplate.exchange("URL",HttpMethod.GET, entity, Jira.class);
System.out.println(result.getBody().toString());
}
}
Any ideas how to solve this?
Thank you.
I have a solution for you. This error appears because Jira API returns a list of objects.
Here is what you should do:
ResponseEntity <Jira[]> result = restTemplate.exchange("URL",HttpMethod.GET, entity, Jira[].class);
List<Jira>=Arrays.asList(result.getBody());
Related
I'm working my way through the book "Mastering Xamarin.Forms" by Ed Snider, which is overall an excellent book. However, I've run into a snag in creating an Azure function app as described in the book. I created the function app (MyTripLogFunctionApp) and a function within it called entry, and I see both of them on portal.azure.com. However, when I try to connect to it from a REST console (I'm using Talend API Tester), I get a 404 error. The URL I'm inputting is, as described in the book, https://mytriplogfunctionapp.azurewebsites.net/api/entry. In Talend API Tester, I set the entry to GET, click on Send, and 404! I've poked around on the web and verified that the version is set to 3, and the function app is set to 64-bit, but no joy. The book is 2 years old, so something may have changed. Anything wrong with how I'm going about this? Here's the code for the entry function:
#r "Newtonsoft.Json"
#r "Microsoft.WindowsAzure.Storage"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using Newtonsoft.Json;
public static async Task<IActionResult> Run(HttpRequest req, Newtonsoft.Json.Linq.JArray entryTableInput,
IAsyncCollector<Entry> entryTableOutput, ILogger log)
{
//log.LogInformation("C# HTTP trigger function processed a request.");
log.LogInformation(req.Method);
if(req.Method == "GET")
{
return (ActionResult) new OkObjectResult(entryTableInput);
}
var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
var entry = JsonConvert.DeserializeObject<Entry>(requestBody);
if(entry != null)
{
await entryTableOutput.AddAsync(entry);
return (ActionResult) new OkObjectResult(entry);
}
return new BadRequestObjectResult("Invalid entry request.");
}
public class Entry{
public string Id => Guid.NewGuid().ToString("n");
public string Title {get; set;}
public double Latitude {get; set;}
public double Longitude {get; set;}
public DateTime Date {get; set;}
public int Rating {get; set;}
public string Notes {get; set;}
public string PartitionKey => "ENTRY";
public string RowKey => Id;
}
And here's the json:
{
"bindings": [
{
"authLevel": "anonymous",
"name": "req",
"type": "httpTrigger",
"direction": "in",
"methods": [
"get",
"post"
]
},
{
"name": "$return",
"type": "http",
"direction": "out"
},
{
"type": "table",
"name": "entryTableOutput",
"tableName": "entry",
"connection": "AzureWebJobsStorage",
"direction": "out"
},
{
"type": "table",
"name": "entryTableOutput",
"tableName": "entry",
"take": 50,
"connection": "AzureWebJobsStorage",
"direction": "in"
}
]
,
"disabled" : false
}
I am generating a JavaClient using an OpenAPISpec document. I have used swagger-codegen 3.0 to generate the code. The OpenAPISpec version is 3.0.1.
Below is the OpenAPI snippet I am facing problems with:
"RequestWithInsuranceInfo": {
"type": "object",
"description": "This request schema will produce a response containing an out of pocket estimate for the given service using the patient's insurance information.",
"additionalProperties": false,
"properties": {
"insuranceInfo": {
"$ref": "#/components/schemas/InsuranceInfo"
},
"service": {
"type": "object",
"additionalProperties": false,
"description": "Schema to use when the patient's benefit info is not given in the request.",
"properties": {
"codes": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ServiceCode"
}
},
"provider": {
"$ref": "#/components/schemas/Provider"
},
"costs": {
"$ref": "#/components/schemas/ServiceCosts"
}
},
"required": [
"codes",
"provider",
"costs"
]
}
}
},
"InsuranceInfo": {
"description": "Information about the payer, plan, and members.",
"additionalProperties": false,
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"title": "Option 1: Patient Is Policy Holder",
"description": "Schema to use when the patient the primary on the insurance plan.",
"properties": {
"payer": {
"$ref": "#/components/schemas/Payer"
},
"policyHolderInfo": {
"$ref": "#/components/schemas/PolicyHolderInfo"
}
},
"required": [
"payer",
"policyHolderInfo"
]
},
{
"type": "object",
"additionalProperties": false,
"title": "Option 2: Patient Is Dependent",
"description": "Schema to use when the patient is a dependent on the insurance plan.",
"properties": {
"payer": {
"$ref": "#/components/schemas/Payer"
},
"dependentMemberInfo": {
"$ref": "#/components/schemas/DependentMemberInfo"
},
"policyHolderInfo": {
"$ref": "#/components/schemas/PolicyHolderInfo"
}
},
"required": [
"payer",
"dependentMemberInfo",
"policyHolderInfo"
]
}
]
},
Below is the code which gets generated:
public class InsuranceInfo implements OneOfInsuranceInfo {
#Override
public boolean equals(java.lang.Object o) {..}
#Override
public int hashCode() {..}
#Override
public String toString() {..}
private String toIndentedString(java.lang.Object o) {..}
}
public interface OneOfInsuranceInfo {
}
public class RequestWithInsuranceInfo implements OneOfRequest {
#SerializedName("insuranceInfo")
private InsuranceInfo insuranceInfo = null;
#SerializedName("service")
private RequestWithInsuranceInfoService service = null;
..
}
public class Payer {
#SerializedName("id")
private String id = null;
..
}
public class PolicyHolderInfo {
#SerializedName("memberId")
private String memberId = null;
#SerializedName("firstName")
private String firstName = null;
#SerializedName("lastName")
private String lastName = null;
#SerializedName("dateOfBirth")
private LocalDate dateOfBirth = null;
..
}
public class DependentMemberInfo {
#SerializedName("memberId")
private String memberId = null;
#SerializedName("firstName")
private String firstName = null;
#SerializedName("lastName")
private String lastName = null;
#SerializedName("dateOfBirth")
private LocalDate dateOfBirth = null;
..
}
As shown, the InsuranceInfo object implements the OneOfInsuranceInfo interface but has no variables. Payer, PolicyHolderInfo and dependentMemberInfo class are generated but they are not linked to the InsuranceInfo class anyhow. How do I populate the InsuranceInfo class?
The issue is probably that the InsuranceInfo schema
"InsuranceInfo": {
"description": "Information about the payer, plan, and members.",
"additionalProperties": false,
"oneOf": [
{ ... },
{ ... }
]
}
effectively disallows ALL properties. This is because additionalProperties: false only knows about the properties defined directly alongside it and has no visibility into oneOf subschemas.
To resolve the issue, you can rewrite the InsuranceInfo schema without oneOf, as follows. This schema is basically "Option 2" from the original schema, except the dependentMemberInfo property is defined as optional.
"InsuranceInfo": {
"description": "Information about the payer, plan, and members.",
"additionalProperties": false,
"type": "object",
"required": [
"payer",
"policyHolderInfo"
],
"properties": {
"payer": {
"$ref": "#/components/schemas/Payer"
},
"dependentMemberInfo": {
"$ref": "#/components/schemas/DependentMemberInfo"
},
"policyHolderInfo": {
"$ref": "#/components/schemas/PolicyHolderInfo"
}
}
}
I have the following ResponseHandler to override a Data Rest Response Handlers. Instead of returning all data, the handler returns only data for the owner (logged in user- currently hardcoded).
package com.osde.prepo.controller;
import com.osde.prepo.entity.Company;
import com.osde.prepo.repository.CompanyRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.RepositoryRestController;
import org.springframework.hateoas.CollectionModel;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
/**
* customer controller to override some Data Rest Response Handlers
*/
#RepositoryRestController
public class CompanyController {
Logger logger = LoggerFactory.getLogger(CompanyController.class);
private final CompanyRepository companyRepository;
#Autowired
public CompanyController(CompanyRepository repo) {
companyRepository = repo;
}
#RequestMapping(method = RequestMethod.GET, value = "/companies")
public #ResponseBody
ResponseEntity<?> getAllCompaniesForCurrentUser() {
logger.info("custom implementation for get called!!");
List<Company> companies = new ArrayList<>();
companies = companyRepository.findByOwnerId("google-oauth2|107634743108791790006");
// convert to HATEOAS
CollectionModel<Company> resources = CollectionModel.of(companies);
resources.add(linkTo(methodOn(CompanyController.class)
.getAllCompaniesForCurrentUser())
.withSelfRel());
return ResponseEntity.ok(resources);
}
}
I get the following response, when consuming the API:
{
"_embedded": {
"companies": [
{
"ownerId": "google-oauth2|107634743108791790006",
"name": "Company 1",
"city": "Ort 1",
"country": "Germany",
"profile": "We are 1 ...",
"logoUrl": null
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/companies"
}
}
}
The original API has the following response (including self-links to the items) How do I have to change my code to get this response (paging is not of interest so far!)?
{
"_embedded": {
"companies": [
{
"ownerId": "google-oauth2|107634743108791790006",
"name": "Company 1",
"city": "Ort 1",
"country": "Germany",
"profile": "We are 1 ...",
"logoUrl": null,
"_links": {
"self": {
"href": "http://localhost:8080/api/companies/1"
},
"company": {
"href": "http://localhost:8080/api/companies/1"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/api/companies/"
},
"profile": {
"href": "http://localhost:8080/api/profile/companies"
},
"search": {
"href": "http://localhost:8080/api/companies/search"
}
},
"page": {
"size": 20,
"totalElements": 1,
"totalPages": 1,
"number": 0
}
}
Here is a sample code to achieve this. I have assumed that your Entity has a getter for id as getId().It can be changed as per the code.
There is a need to extend this RepresentationModel on the entity class to get the add method.
eg
public class Company extends RepresentationModel<Company> implements Serializable {
#RequestMapping(method = RequestMethod.GET, value = "/companies")
public #ResponseBody
ResponseEntity<?> getAllCompaniesForCurrentUser() {
logger.info("custom implementation for get called!!");
List<Company> companies = companyRepository.findByOwnerId("google-oauth2|107634743108791790006");
for (Company company : companies) {
Link selfLink = linkTo(methodOn(CompanyController.class)
.getCompaniesById(company.getId())).withSelfRel();
company.add(selfLink);
}
Link link = linkTo(methodOn(CompanyController.class).getAllCompaniesForCurrentUser()).withSelfRel();
CollectionModel<Company> result = CollectionModel.of(companies, link);
return ResponseEntity.ok().body(companies);
}
There is an another way to do this as well. This won't require any change on the Entity class.
import org.springframework.hateoas.EntityModel;
#RequestMapping(method = RequestMethod.GET, value = "/companies")
public #ResponseBody
ResponseEntity<?> getAllCompaniesForCurrentUser() {
logger.info("custom implementation for get called!!");
List<EntityModel<Company>> companies = companyRepository
.findByOwnerId("google-oauth2|107634743108791790006")
.stream()
.map(this::generateLinks)
.collect(Collectors.toList());
CollectionModel<EntityModel<Company>> resource = CollectionModel.of(companies);
resource.add(entityLinks.linkToCollectionResource(Company.class));
resource.add(entityLinks.linksToSearchResources(Company.class));
return ResponseEntity.ok().body(resource);
}
private EntityModel<Company> generateLinks(Company company) {
EntityModel<Company> resource = EntityModel.of(company);
resource.add(entityLinks.linkToItemResource(Company.class, company.getId()).withSelfRel());
resource.add(entityLinks.linkToCollectionResource(Company.class));
resource.add(entityLinks.linksToSearchResources(Company.class));
return resource;
}
That points to the correct direction!
Now I get the following output:
[
{
"companyId": 1,
"ownerId": "google-oauth2|107634743108791790006",
"name": "Company 1",
"city": "Ort 1",
"country": "Germany",
"profile": "We are 1 ...",
"logoUrl": null,
"links": [
{
"rel": "self",
"href": "http://localhost:8080/api/companies/1"
},
{
"rel": "companies",
"href": "http://localhost:8080/api/companies{?page,size,sort}"
}
]
}
]
How to adjust the following aspects:
link to companies should be on the collection-level and just be http://localhost:8080/api/companies
rename links to _links
I am using Spring Data JPA and Spring Data Rest with Spring Boot 1.5.10. I have three classes annotated with #Entity: Message, AMessage, and BMessage (Figure 2, 3, 4). Classes AMessage and BMessage extend Message class (Figure 1). I have one MessageRepository that is exposed via Spring Data Rest. When I make a request to get all messages at http://host:port/messages, I get a response that contains two separate arrays under _embedded object (one for AMessage and one for BMessage) even though I am retrieving data from the /messages endpoint (Figure 5). I only want to retrieve columns from the Message Entity. How can this be achieved?
I have uploaded my code to github.com
Figure 1: Hierarchy
Message
|
----------------------
| |
AMessage BMessage
Figure 2: Message Class (Parent)
import javax.persistence.*;
#Entity
#Table(name = "MSG")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "TYPE")
public class Message {
#Id #GeneratedValue
private Long id;
private String messageColumn1;
#Column(name = "TYPE", updatable = false, insertable = false)
private String messageType;
public String getMessageColumn1() {
return messageColumn1;
}
public void setMessageColumn1(String messageColumn1) {
this.messageColumn1 = messageColumn1;
}
public String getMessageType() {
return messageType;
}
}
Figure 3: AMessage Class (Child)
import javax.persistence.*;
#Entity
#Table(name = "MSG_A")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorValue("A")
public class AMessage extends Message {
private String messageAColumn1;
public String getMessageAColumn1() {
return messageAColumn1;
}
public void setMessageAColumn1(String messageAColumn1) {
this.messageAColumn1 = messageAColumn1;
}
}
Figure 4: BMessage Class (Child)
import javax.persistence.*;
#Entity
#Table(name = "MSG_B")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorValue("B")
public class BMessage extends Message {
private String messageBColumn1;
public String getMessageBColumn1() {
return messageBColumn1;
}
public void setMessageBColumn1(String messageBColumn1) {
this.messageBColumn1 = messageBColumn1;
}
}
Figure 5: Get messages response (aMessages and bMessages separated)
http://localhost:8080/messages
{
"_embedded": {
"aMessages": [
{
"messageColumn1": "MColumn1",
"messageType": "A",
"messageAColumn1": "AColumn1",
"_links": {
"self": {
"href": "http://localhost:8080/aMessage/1"
},
"aMessage": {
"href": "http://localhost:8080/aMessage/1"
}
}
}
],
"bMessages": [
{
"messageColumn1": "MColumn1",
"messageType": "B",
"messageBColumn1": "BColumn1",
"_links": {
"self": {
"href": "http://localhost:8080/bMessage/2"
},
"bMessage": {
"href": "http://localhost:8080/bMessage/2"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/messages{?page,size,sort}",
"templated": true
},
"profile": {
"href": "http://localhost:8080/profile/messages"
}
},
"page": {
"size": 20,
"totalElements": 2,
"totalPages": 1,
"number": 0
}
}
Resolved! Created new repositories for AMessage and BMessage. Annotated each repository with #RepositoryRestResource(collectionResourceRel = "messages"). This caused the messages to be combined into a single array under the _embedded object, when performing a GET on http://localhost:8080/messages
{
"_embedded": {
"messages": [
{
"messageColumn1": "MColumn1",
"messageType": "A",
"messageAColumn1": "AColumn1",
"_links": {
"self": {
"href": "http://localhost:8080/aMessages/1"
},
"aMessage": {
"href": "http://localhost:8080/aMessages/1"
}
}
},
{
"messageColumn1": "MColumn1",
"messageType": "B",
"messageBColumn1": "BColumn1",
"_links": {
"self": {
"href": "http://localhost:8080/bMessages/2"
},
"aMessages": {
"href": "http://localhost:8080/bMessages/2"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/messages{?page,size,sort}",
"templated": true
},
"profile": {
"href": "http://localhost:8080/profile/messages"
}
},
"page": {
"size": 20,
"totalElements": 2,
"totalPages": 1,
"number": 0
}
}
I´m using Spring Boot and HATEOAS to build a REST API and I am struggling with the curie creation. The Spring HATEOAS guide says that in order to automatically insert a curie in the responses, you should do the following:
#Configuration
#EnableWebMvc
#EnableHypermediaSupport(type= {HypermediaType.HAL})
public class Config {
#Bean
public CurieProvider curieProvider() {
return new DefaultCurieProvider("ex", new UriTemplate("http://www.example.com{#rel}"));
}
}
My config class is like this:
#SpringBootApplication
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
#Bean
public CurieProvider curieProvider() {
return new DefaultCurieProvider("xpto", new UriTemplate("http://www.xpto.com{#rel}"));
}
}
I tried to add the #EnableWebMvc to my config class but it changes the rendering of the response (hal) and the curie doesn´t appear. I have to do something on the controller to create the curie?
Update:
I updated the Spring Hateoas (to 0.17.0.RELEASE) and now my collection name is rendered with the curie but the curie does not appear in the _links section:
{
"_links": {
"self": {
"href": "http://localhost:8080/technologies"
}
},
"_embedded": {
"mycurie:technology": [
{
"id": 1,
"description": "A",
"_links": {
"self": {
"href": "http://localhost:8080/technologies/1"
}
}
},
{
"id": 2,
"description": "B",
"_links": {
"self": {
"href": "http://localhost:8080/technologies/2"
}
}
}
]
}
}
If I add one link to the _links section then the curie link appears:
{
"_links": {
"self": {
"href": "http://localhost:8080/technologies"
},
"mycurie:xpto": {
"href": "http://localhost:8080/xpto"
},
"curies": [
{
"href": "http://localhost:8080/rels/{rel}",
"name": "mycurie",
"templated": true
}
]
},
"_embedded": {
"mycurie:technology": [
{
"id": 1,
"description": "A",
"_links": {
"self": {
"href": "http://localhost:8080/technologies/1"
}
}
},
{
"id": 2,
"description": "B",
"_links": {
"self": {
"href": "http://localhost:8080/technologies/2"
}
}
}
]
}
}
This is my controller:
#RestController
#ExposesResourceFor(Technology.class)
#RequestMapping(value = "/technologies")
public class TechnologyRestController {
...
#RequestMapping(method = RequestMethod.GET, produces = "application/vnd.xpto-technologies.text+json")
public Resources<TechnologyResource> getAllTechnologies() {
List<Technology> technologies = technologyGateway.getAllTechnologies();
Resources<TechnologyResource> technologiesResources = new Resources<TechnologyResource>(technologyResourceAssembler.toResources(technologies));
technologiesResources.add(linkTo(methodOn(TechnologyRestController.class).getAllTechnologies()).withSelfRel());
return technologiesResources;
}
}
Your config class looks correct to me, however Spring Boot's HypermediaAutoConfiguration requires org.springframework.plugin:spring-plugin-core, an optional dependency of Spring HATEOAS, to be on the classpath for it to be enabled. I'd guess that you're missing this dependency. Try adding a dependency on org.springframework.plugin:spring-plugin-core:1.1.0.RELEASE.