Get null property when trying to use JSON views on rest Controller just extending from RestfulController - rest

I'm trying out JSON views, not on top of domain class using #Resource, but by creating a RestfulController and trying to render that using JSON views. I've added all the relevant dependencies in build config.
I have a domain Post class like this (which I didn't want to directly expose)
class Post implements Serializable {
Map comments
User user
Venue venue
String description
Rating rating //should this be an enum?
LocalDateTime dateCreated
LocalDateTime lastUpdated
static belongsTo = [user:User]
static hasOne = [rating:Rating]
static constraints = {
venue nullable:true
comments nullable:true
description nullable:true
rating nullable:true, lazy:false
}
static mapping = {
//set the sort order for Posts - default using newest post first
sort dateCreated :"desc"
}
}
So I then created a default RestfulController like this:
class PostRestController extends RestfulController {
static responseFormats = ["json", "xml"]
//constructor - tells rest controller which domain class to scaffold
PostRestController() {
super (Post)
}
}
I'm not overriding any of the default scaffolding methods here.
When I used a rest client to access the default (I've mapped /api/posts (resources: postRest in the UrlMappings). When I access the URL with my REST client I got the full dump of the Post (including comments field persisted in a map) - this looks like this in my rest client - all OK:
[
{
"id": 1,
"comments": {
"view": "lovely"
},
"dateCreated": {
"class": "java.time.LocalDateTime",
"dayOfMonth": 7,
"dayOfWeek": {
"enumType": "java.time.DayOfWeek",
"name": "TUESDAY"
},
"dayOfYear": 66,
"hour": 19,
"minute": 15,
"month": {
"enumType": "java.time.Month",
"name": "MARCH"
},
"monthValue": 3,
"nano": 263000000,
"second": 10,
"year": 2017,
"chronology": {
"calendarType": "iso8601",
"class": "java.time.chrono.IsoChronology",
"id": "ISO"
}
},
"description": null,
"lastUpdated": {
"class": "java.time.LocalDateTime",
"dayOfMonth": 7,
"dayOfWeek": {
"enumType": "java.time.DayOfWeek",
"name": "TUESDAY"
},
"dayOfYear": 66,
"hour": 19,
"minute": 15,
"month": {
"enumType": "java.time.Month",
"name": "MARCH"
},
"monthValue": 3,
"nano": 263000000,
"second": 10,
"year": 2017,
"chronology": {
"calendarType": "iso8601",
"class": "java.time.chrono.IsoChronology",
"id": "ISO"
}
},
"rating": null,
"user": {
"id": 1
},
"venue": null
}
],
I then tried to add JSON views on top of this in the grails-app/views/postRest folder.
I did a really simple template _post.gson like this:
model {
Post post
}
json {
comments post.comments
description post.description
//rating post.rating
userWhoPosted "${post?.user}"
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MMM-dd")
def when = post.dateCreated.format(formatter)
created when
}
I then added an index.gson to render the template:
model {
List<Post> postList
}
//call the template to iterate over the postList to produce the output
json g.render(postList)
This breaks the server with this stacktrace and a 500 error to the REST client. If I comment out the line in_post.gson relating to user it all works. Leave it in and it fails:
Caused by: grails.views.ViewRenderException: Error rendering view: null
at grails.views.AbstractWritableScript.writeTo(AbstractWritableScript.groovy:43)
at grails.views.mvc.GenericGroovyTemplateView.renderMergedOutputModel(GenericGroovyTemplateView.groovy:73)
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303)
at grails.views.mvc.renderer.DefaultViewRenderer.render(DefaultViewRenderer.groovy:111)
at grails.artefact.controller.RestResponder$Trait$Helper.internalRespond(RestResponder.groovy:188)
at grails.artefact.controller.RestResponder$Trait$Helper.respond(RestResponder.groovy:62)
at grails.rest.RestfulController.index(RestfulController.groovy:64)
at grails.transaction.GrailsTransactionTemplate$2.doInTransaction(GrailsTransactionTemplate.groovy:96)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
at grails.transaction.GrailsTransactionTemplate.execute(GrailsTransactionTemplate.groovy:93)
... 4
If I comment out the post.user (and ratings reference) it works OK, but when I try and post the post.user it fails with the above. There was a note in the docs about ensuring that your query pulled the refs with a fetch join - so I tried to provide a override to ensure I returned the fetch join - all I get is empty returned to the client:
class PostRestController extends RestfulController {
static responseFormats = ["json", "xml"]
//constructor - tells rest controller which domain class to scaffold
PostRestController() {
super (Post)
}
def index() {
Collection<Post> res = Post.list([fetch:[user:"join",rating:"join"]])
res
}
}
Why when I do it without the JSON view it works fine and when I use the JSON view I can't get the output including references? I checked the list request and it returns the list successfully in the debugger - but breaks in the rendering.
If I can get this to work, JSON views on Grails 3.2.6 looks pretty nice.

Aaargh - think this issue is with jsonViews 1.1.5 - its not ready for java 8 LocalDateTime.
i saw a trace on stackoverflow see topic
hibernate will now take localdateTime in your domain classes - that works. But the json template rendering wont, even if you add the java8 plugin.
So i went back into domain class changed my LocalDateTime back to Date, also changed the json template to use the older SimpleDateTime format (instead of DateTimeFormatter) and re ran - low and behold it worked.
I'll be really glad when we can say grails is properly java8 ready.
apparently json views requires feature enablement which is due in json views 2 (think this is an M2 right now) - so i've had to revert to java 7 Date until then.
blimey another lost day in the weeds.

Related

Strapi API Rest V 3.6.8 how to search posts by title?

I have installed version 3.6.8 of Strapi
In the docs for v3.x
https://strapi.gitee.io/documentation/v3.x/content-api/parameters.html#filter
Filters are used as a suffix of a field name:
No suffix or eq: Equals
ne: Not equals
lt: Less than
gt: Greater than
lte: Less than or equal to
gte: Greater than or equal to
in: Included in an array of values
nin: Isn't included in an array of values
contains: Contains
ncontains: Doesn't contain
containss: Contains case sensitive
ncontainss: Doesn't contain case sensitive
null: Is null/Is not null
And I can see those examples
GET /restaurants?_where[price_gte]=3
GET /restaurants?id_in=3&id_in=6&id_in=8
etc..
So I tried
/posts?_where[title_contains]=foo
/posts?title_contains=foo
And I also tried the "new way" in V4
/posts?filters[title][contains]=foo
But all of this attempts return all the post, exactly the same than just doing
/posts?
Any idea how to filter by post title and/or post body?
Almost there my friend! The issue you are facing called deep filtering (please follow the link for documentation).
In Short: the title field is located inside the attributes object for each item
Your items may look something similar to this:
{
"data": [
{
"id": 1,
"attributes": {
"title": "Restaurant A",
"description": "Restaurant A's description"
},
"meta": {
"availableLocales": []
}
},
{
"id": 2,
"attributes": {
"title": "Restaurant B",
"description": "Restaurant B's description"
},
"meta": {
"availableLocales": []
}
},
]
}
And therefor the filter should be
/api/posts?filters[attributes][title][$contains]=Restaurant
Also note:
the $ sign that should be included for your operator (in our case contains)
the api prefix you should use before the plural api id (e.g. posts, users, etc.)
you may prefer using $containsi operator in order to ignore upper and lower case letters (better for searching operations)
Let me know if it worked for you!

Magento 2.4 API, POST product custom_attributes

I need to add/update products using the REST API. I cannot figure how to add a custom_attributes, knowing only the value/label of the attribute and not the ID (attribute of type 'dropdown' or 'selectable')
POST /rest/all/V1/products
{
"product": {
"sku": "D119",
"custom_attributes": [{
"attribute_code": "year",
"value": "2015"
}]
}
}
Upper code works fine if I just put in the ID in the value field. But a sourcing system wouldnt know the id's....
Side note: The M2 backend import function is capable of importing value/label of the attribute? How come?

Symfony 4: Implement links routes with Hateoas-Bundle

I try the install "willdurand/hateoas-bundle" inside my project with Symfony4 but there is a problem because the "HATEOAS-BUNDLE" only working with Symfony3 and below. requires symfony/framework-bundle ~2.3 - v3.4.3
In this case I used willdurand/hateoas but I checked and there is not have BazingaHateoasBundle and I think there is my problem because I can not perform the Serialization links in this way:
{
"user": {
"id": 3,
"played_name": "John",
"_links": {
"self": { "href": "http://example.com/person/3" }
}
}
}
It only shows me the data without serialization links:
{
"id": 3,
"played_name": "John Doe"
}
In Symfony 3 and below, the hateos-bundle include this package BazingaHateoasBundle):
{
public function registerBundles()
{
$bundles = array(new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle());
}
}
And the willdurand/Hateoas for Symfony 4 there is not include the BazingaHateoasBundle
And When configure the Serialization Link in my Entity Role
#Hateoas\Relation("person", href=#Hateoas\Route("get_human", parameters={"person" = "expr(object.getPerson().getId())"})
The response is only the data without serialization link:
Postman Response
There would be a way for serialization links to be made with Hateoas in Symfony 4?

Property named "type" on embedded records with RESTSerializer in Ember Data - Error no model was found

I use Ember Data 1.13.4.
I have a model with some embedded records, let's say they look like this:
var Partner = DS.Model.extend({
name: DS.attr(),
addresses: DS.hasMany('address', { async: false } ),
});
var Address = DS.Model.extend({
type: DS.attr(),
zip: DS.attr(),
city: DS.attr(),
street: DS.attr()
});
The API sends back the Address records embedded in the Partner records. Example response:
{
"partners": [
{
"id": 47,
"name": "Johnny",
"addresses": [
{
"id": 7,
"type": "MAIN",
"zip": "1234",
"city": "City-X",
"street": "6. Nowhere"
}
],
},
]
}
The problem is that type on the Address model is just a normal property, but Ember Data wants it to be the type of the embedded model, and I get this assertion message:
Error: No model was found for 'MAIN'
Where 'MAIN' is the content of the type property.
I can't change how the API sends back data, I need to adapt to it.
How do I do this?
Edit: Important detail which I forgot to include the first time.
The API has a /partners/search endpoint, which I access with a direct ajax request, then I (supposedly) need to call this.store.pushMany('partner', this.store.normalize('partner', response.partners)); And this is when the No model was found for 'MAIN' is raised.
Your { partners: { addresses: [ ... ] } } is returning an embedded data, but because you are providing an object with id and type ember-data is understanding that the api returns an polymorphic association. And trying to find a model called MAIN, but since it does't exist Error: No model was found for 'MAIN' is raised.
In order to tell ember-data that your relationship is embedded you need to override the PartnerSerializer and include the DS.EmbeddedRecordsMixin.
App.ApplicationAdapter= DS.RESTAdapter;
App.ApplicationSerializer = DS.RESTSerializer;
App.PartnerSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
addresses: { embedded: 'always' }
}
})
A live demo of this sample running http://emberjs.jsbin.com/nufofehota/1/edit?html,js,output

JsonMappingException: Already had POJO for id

I have an error when trying to work with #JsonIdentityInfo jackson annotation. When I try to deserialize the object I get the following exception:
Could not read JSON: Already had POJO for id (java.lang.Integer) [1] (through reference chain: eu.cobiz.web.domain.Site["operators"]->eu.yavix.web.domain.Account["image"]->eu.cobiz.web.domain.Image["#Image"]);nested exception is com.fasterxml.jackson.databind.JsonMappingException: Already had POJO for id (java.lang.Integer) [1] (through reference chain: eu.yavix.web.domain.Site["operators"]->eu.cobiz.web.domain.Account["image"]->eu.cobiz.web.domain.Image["#Image"])
The JSON I am trying to deserialize looks like:
{
"#Site": 1,
"siteId": 1,
"name": "0",
"address": {
"#Address": 2,
"addressId": 4,
"number": "22"
},
"operators": [
{
"accountId": 1,
"email": "user982701361#yavix.eu",
"image": {
"#Image": 1,
"imageId": 1,
"uri": "http://icons.iconarchive.com/icons/deleket/purple-monsters/128/Alien-awake-icon.png"
}
},
{
"accountId": 2,
"email": "user174967957#yavix.eu",
"image": {
"#Image": 2,
"imageId": 2,
"uri": "http://icons.iconarchive.com/icons/deleket/purple-monsters/128/Alien-awake-icon.png"
}
}
]
}
My domain object is annotated with
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#Image")
The problem arises on #Id annotation since if I remove the annotation the problem disappears (as I did for account) but on my understanding the new feature is useful for cyclic dependencies which is useful for me in other scenarios. There shouldn't be a conflict between the 2 images since they are different objects.
How can I solve this or what is the problem?
You should use scope parameter when annotating the ids. Then the de-serializer would make sure the id is unique within the scope.
From Annotation Type JsonIdentityInfo:
Scope is used to define applicability of an Object Id: all ids must be unique within their scope; where scope is defined as combination of this value and generator type.
e.g.
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class,property="#id", scope = Account.class)
To avoid id conflict try to use ObjectIdGenerators.PropertyGenerator.class or ObjectIdGenerators.UUIDGenerator.class instead of ObjectIdGenerators.IntSequenceGenerator.class