Can there be two ids for an object in json api - java

I am using a restful webservice that gives response in json api format. There is a relationship attribute that has id and type params. Based on the id reference it displays values in the included attribute. The id is created after the two requests that process as a final output. Till then I save my data in database as one single object. Now when I fetch the data from database using rest webservice the output shows all the attributes except the included. Which I believe is because it isn't able to find the reference so not getting displayed. But in the database all the values are present perfectly. I am not sure whether json api supports multiple ids for relationship attribute or not.
Example:
Request Body:
{
"data": {
"type": "orders",
"attributes": {
"name": "new order",
"updateDate": "",
"register":"yes",
"items":[
{
"description": "newly added item",
"type": "new item",
"amount": [
{
"deliveryfee": "123",
"mrp": "456"
}
]
}
]
}
}
}
Expected Response Body:
{
"data": {
"type": "orders",
"id": "1",
"attributes": {
"name": "new order",
"updateDate": "",
},
"relationships": {
"items": {
"data": [
{
"type": "items",
"id": null
}
]
}
}
},
"included": [
{
"type": "items",
"id": null,
"attributes": {
"type": "new item",
"description": "newly added item",
"amount": [
{
"deliveryfee": "123",
"mrp": "456"
}
]
}
}
]
}
Actual Response Body:
{
"data": {
"type": "orders",
"id": "1",
"attributes": {
"name": "new order",
"updateDate": "",
},
"relationships": {
"items": {
"data": [
{
"type": "items",
"id": null
}
]
}
}
}
}

I'm not fully sure if I understand your question correctly. But let me try to answer.
The combination of a type and id is used in JSON API specification to identify a resource:
Within a given API, each resource object’s type and id pair MUST identify a single, unique resource.
null as used in your example is not a valid value for id:
The values of the id and type members MUST be strings.
The API may combine multiple identifiers used in its internal database to construct the value used for id in a JSON API document as long as it's unique for the given type. This would be a valid id value from JSON API specification point of view as long as it's guaranteed to be unique.
{
"type": "posts":
"id": "post_id:5,locale:en"
}
The API may de-serialize the ID to two different identifiers: A post with the id 5 and a local with the id "en". That would be an internal implementation detail of the API. Consumers should not care if a meaning is encoded within the id value.
The request and response bodies given in your question do not fit together. Both contain a field items. But in the request body the items field is an attribute, while its a relationship in the response.
It seems as if you are trying to create multiple resources at once. This is not support by JSON API specification v1. It is supported in the third release candidate for v1.1 of the specification through the official extension Atomic Operations.

Related

Query Elastic DSL - Search query using spring boot data

I have the following properties file generated via Java and spring boot data elasticsearch. The file is generated in a User.java class and the property "friends" is a List where Friends is a Fiends.java file, both class file act as the model. Essentially I want to produce a select statement but in Query DSL Language using Spring Boot Data. The index is called user.
So I am trying to achieve the following SELECT * FROM User where (userName ="Tom" OR nickname="Tom" OR friendsNickname="Tom") AND userID="3793"
or (verbose-dsl)
match where (userName="Tom" OR nickname="Tom" OR friendsNickname="Tom") AND userID="3793"
"mappings": {
"properties": {
"_class": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"userName": {
"type": "text"
},
"userId": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"friends": {
"type": "nested",
"properties": {
"firstName": {
"type": "text"
},
"lastName": {
"type": "text"
},
"age": {
"type": "text"
},
"friendsNickname": {
"type": "text"
}
}
},
"nickname": {
"type": "text"
}
}
}
I have tried the following code but return 0 hits back from a elastic search but no dice returns no hits
BoolQueryBuilder query =
QueryBuilders.boolQuery()
.must(
QueryBuilders.boolQuery()
.should(QueryBuilders.matchQuery("userName", "Tom"))
.should(QueryBuilders.matchQuery("nickname", "Tom"))
.should(
QueryBuilders.nestedQuery(
"friends",
QueryBuilders.matchQuery("friendsNickname", "Tom"),
ScoreMode.None)))
.must(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("userID", "3793")));
Apologies if this seems like a simple question, My knowledge on ES is quite thin, sorry if this may seem like an obvious answer.
Great start!!
You just have a tiny mistake on the following line where you need to prefix the field name by the nested field name, i.e. friends.friendsNickname
...
QueryBuilders.matchQuery("friends.friendsNickname", "Tom"),
... ^
|
prefix
Also you have another typo where the userID should read userId according to your mapping.
Use friends.friendsNickname and also user termsQuery on userId.keyword
`
.must(QueryBuilders.boolQuery()
.should(QueryBuilders.matchQuery("userName", "Tom"))
.should(QueryBuilders.matchQuery("nickname", "Tom"))
.should(QueryBuilders.matchQuery("friends.friendsNickname", "Tom"))
)
.must(QueryBuilders.termsQuery("userId.keyword", "3793"));
`
Although I recommend changing userName, userID to keyword.
"userId": {
"type": "keyword",
"ignore_above": 256,
"fields": {
"text": {
"type": "text"
}
}
}
Then you don't have to put keyword so you just have to put userId instead of userId.keyword. If you want to have full-text search on the field is use userId.text. The disadvantage of having a text type is that you can't use the field to sort your results that's why I encourage ID fields to be of type keyword.

Is it possible to create dynamic pojo class according dynamic json response everytime?

An api provides different result in json everytime I hit. How can I generate a new model class everytime according to json response?
Is it possible to create dynamic pojo class according dynamic json response everytime??
on 1st hit -
{
"Banners": [
{
"banner_id": "30",
"banner_image": ""
}
],
"success": "success",
"message": "Banners"
}
on 2nd hit
{
"Banners": [
{
"banner_desc": "returns between paragraphs",
"banner_type": "regular",
"banner_size": 100,
}
],
"success": "success",
"message": "Banners"
}
on 3rd hit -
{
"user_id": "4",
"name": "",
"email": "k#gmail.com",
"mobile": "",
"image": "",
"success": "success",
"message": "Registered"
}

How to use XML as a data field in a JSON request in OpenAPI/Swagger?

I need to create a REST endpoint which will consume a JSON request but this JSON object has a data field which is an XML file.
As shown in the example below, Customer is the request object which can hold address in JSON format (AddressJson) or XML format (AddressXML).
Can anyone suggest how I can use address in XML format (AddressXML)? In the end, I want an OpenAPI (Swagger) representation*.
"Customer": {
"description": "A problem object",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "name of the customer",
"example": "null"
},
"AddressJson": {
"$ref": "#/definitions/AddressJson/"
},
"AddressXML": {
"type": "XML",
"description": "address of the customer, specifically in XML format"
}
}
}

Make Elastic Search return certain items from a list

I have list of complex object (address in below example) where each object has attributes as {string,string,string,string,string}.
I want to do filtering on the list and instead of entire document, I just want to get 2 items from the list in the response. Is it possible?
e.g.
Customer:{
id,
address-list:{
street,
city,
postal,
state,
country
}
}
If we have 5 addresses and we want to apply filter on the city value of the address object, is it possible in elastic search?
I know we have include and exclude using which we can filter on the attribute but can we also filter based on attribute value? Instead of getting all the 5 addresses present in a document, can we pull all the attributes but for addresses, we pull items having city="Chicago" in the output of elastic?
Nested Mapping looks like:
{
"mappings": {
"employee":
{
"properties":
{
"addrBean": { "type": "nested",
"properties": {
"addrId": { "type": "integer" },
"city": { "type": "string" },
"state": { "type": "string" },
"country": { "type": "string" }
} } } }
}
}

Swagger: Spring MVC models in GET request

In MVC, you would expect that controller will receive models as input and produce models as output. In Swagger, the latter is not a problem but I have troubles with former. I can't understand how to make Swagger build an input model from incoming GET parameters.
Consider:
"paths": {
"/search": {
"get": {
"consumes": [],
"produces": [
"application/json"
],
"parameters": [
// What goes here?
],
"responses": {
"200": {
"description": "Success",
"schema": {
"$ref": "#/definitions/SearchResponse"
}
},
}
}
}
}
How do I make the generated controller's method to have a signature like:
public ResponseEntity<ResultModel> controllerGet(ModelFromParameters input);
where ModelFromParameters will have several fields corresponding to different GET parameters.
Examples mostly focus either on POST requests, or GET requests where every of 20-odd parameters are stuffed in the arguments list of method, which is obviously anti-MVC.
The API in question is a complex stateless querying system with a lot of parameters.
Assumption: you want to create an object as parameter to your service method but still pass each of the fields of the object as query parameter in the actual http request and be able to document individual fields in swagger.
e.g. GET http://localhost:8080/search?parameter1=value1&parameter2=value2
Typical Service Method Definition where each query param is defined as a parameter in the actual method
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<String>> search(#RequestParam parameter1, #RequestParam parameter2) {
...
}
Modified service method with single parameter using Object (a.k.a Bean) annotated with #ModelAttribute. Though it is an object, as far as the REST API is concerned it is still the same as above.
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<String>> search(#ModelAttribute FormParam formParam) {
...
}
FormParam class. You can document each field using #ApiParam
public class FormParam {
#ApiParam(value = "parameter1 - description here", required = true)
private String parameter1;
#ApiParam(value = "parameter2 - description here", required = true)
private String parameter2;
//define getter setters below
}
This is how it appears in swagger
This is the generated swagger.json snippet
"paths": {
"/search": {
"get": {
"tags": ["search-service"],
"summary": "Search with Object as parameter",
"description": "Search with Object as parameter",
"operationId": "searchUsingGET",
"consumes": ["application/json"],
"produces": ["*/*"],
"parameters": [{
"name": "parameter1",
"in": "query",
"description": "parameter1 - description here",
"required": true,
"type": "string"
},
{
"name": "parameter2",
"in": "query",
"description": "parameter2 - description here",
"required": true,
"type": "string"
}],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
},
"401": {
"description": "Unauthorized"
},
"403": {
"description": "Forbidden"
},
"404": {
"description": "Not Found"
}
}
}
},
If you are manually creating the swagger.json the query parameters can be documented like
"parameters": [{
"name": "parameter1",
"in": "query",
"description": "parameter1 - description here",
"required": true,
"type": "string"
},
{
"name": "parameter2",
"in": "query",
"description": "parameter2 - description here",
"required": true,
"type": "string"
}],
I think you can't. I had also a similar problem, for list page + search, using GET with body. No way to make swagger represent this, also if it works in Tomcat; also Elasticsearch supports it. It looks like there is no plan to change this aspect in swagger. I resorted to split the two in swagger: list-without-search as GET and list+search as POST, just to put the page in the swagger documentation, also if the latter actually works also as GET.
I have no experience with swagger code generation but, if your configuration generates what you expect with POST but it doesn't with GET, then you are probably hitting the same limitation.
https://github.com/swagger-api/swagger-ui/issues/2867
https://github.com/swagger-api/swagger-ui/issues/2136
https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.md#operationRequestBody
The requestBody is only supported in HTTP methods where the HTTP 1.1 specification RFC7231 has explicitly defined semantics for request bodies. In other cases where the HTTP spec is vague, requestBody SHALL be ignored by consumers.
In usual, http server(eg. tomcat, jetty) will not accept a body message within a get request. If client needs pass parameters to server by http get method, it should use query string . Last part of the url after '?' character. Query string details you can see query string
But with spring mvc help, your http request parameters in query string will be bound to your controller method parameter`s fields.So it seems like client passed a pojo parameter to server side.
Any way the parameter part should look like this:
"parameters" : [ {
"name" : "age",
"in" : "query",
"required" : false,
"type" : "integer"
}, {
"name" : "firstName",
"in" : "query",
"required" : false,
"type" : "string"
}]
Please note that "in" filed's value is "query". That means parameter is passed by query string.
I supose what you require is posting your arguments in request body, it goes like this:
...
{
"name": "files",
"in": "body",
"required": true,
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/FileRequest"
}
}
}
which translates to:
public Response storageFilePost(
#NotEmpty(message = "systemID is required!") #QueryParam("systemID") final String systemID,
#NotNull(message = "qParam is required!") #QueryParam("qParam") final Long qParam,
#NotNull(message = "files are required!") final List<FileRequest> files) {

Categories