devour-client
devour-client copied to clipboard
Recursive/Arc Relationships
Hello, First of all, I thanks for your DEVOUR. devour json api is a great package, I think. I've seen all approaches in the JavaScript client implementations but jsonapi-datastore and json-api-store are a little useful and the others not.
Finally, I 've determined to use your package DEVOUR.
it works fine so far. but one issue.
In my database, there is "categories" table and it represents both category and subcategory using parent_id field(Arc Model).
and with ruby on rails, I've built JSON API Server.
when hits the url http://192.168.0.124:3003/api/v1/categories?include="sub-categories", it returns as follows. ( "id" : "1", category, in the sub-categories section, it has sub categories information)
{
"data": [
{
"id": "1",
"type": "categories",
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/1"
},
"attributes": {
"name": "Cat1",
"category-type": 0,
"sort": 1,
"deleted": false,
"created-at": "2017-07-31T10:48:48.000Z",
"updated-at": "2017-07-31T10:48:50.000Z"
},
"relationships": {
"sub-categories": {
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/1/relationships/sub-categories",
"related": "http://192.168.0.124:3003/api/v1/categories/1/sub-categories"
},
"data": [
{
"type": "categories",
"id": "3"
},
{
"type": "categories",
"id": "4"
}
]
},
"parent": {
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/1/relationships/parent",
"related": "http://192.168.0.124:3003/api/v1/categories/1/parent"
}
},
"items": {
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/1/relationships/items",
"related": "http://192.168.0.124:3003/api/v1/categories/1/items"
}
}
}
},
{
"id": "2",
"type": "categories",
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/2"
},
"attributes": {
"name": "Cat2",
"category-type": 0,
"sort": 2,
"deleted": false,
"created-at": "2017-07-31T11:21:14.000Z",
"updated-at": "2017-07-31T11:21:15.000Z"
},
"relationships": {
"sub-categories": {
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/2/relationships/sub-categories",
"related": "http://192.168.0.124:3003/api/v1/categories/2/sub-categories"
},
"data": []
},
"parent": {
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/2/relationships/parent",
"related": "http://192.168.0.124:3003/api/v1/categories/2/parent"
}
},
"items": {
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/2/relationships/items",
"related": "http://192.168.0.124:3003/api/v1/categories/2/items"
}
}
}
},
{
"id": "3",
"type": "categories",
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/3"
},
"attributes": {
"name": "Sub Cat1",
"category-type": 1,
"sort": 1,
"deleted": false,
"created-at": "2017-07-31T11:27:21.000Z",
"updated-at": "2017-07-31T11:27:22.000Z"
},
"relationships": {
"sub-categories": {
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/3/relationships/sub-categories",
"related": "http://192.168.0.124:3003/api/v1/categories/3/sub-categories"
},
"data": []
},
"parent": {
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/3/relationships/parent",
"related": "http://192.168.0.124:3003/api/v1/categories/3/parent"
}
},
"items": {
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/3/relationships/items",
"related": "http://192.168.0.124:3003/api/v1/categories/3/items"
}
}
}
},
{
"id": "4",
"type": "categories",
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/4"
},
"attributes": {
"name": "Sub Cat2",
"category-type": 1,
"sort": 1,
"deleted": false,
"created-at": "2017-07-31T11:27:54.000Z",
"updated-at": "2017-07-31T11:27:55.000Z"
},
"relationships": {
"sub-categories": {
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/4/relationships/sub-categories",
"related": "http://192.168.0.124:3003/api/v1/categories/4/sub-categories"
},
"data": []
},
"parent": {
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/4/relationships/parent",
"related": "http://192.168.0.124:3003/api/v1/categories/4/parent"
}
},
"items": {
"links": {
"self": "http://192.168.0.124:3003/api/v1/categories/4/relationships/items",
"related": "http://192.168.0.124:3003/api/v1/categories/4/items"
}
}
}
}
]
}
and in the client side,
this.jsonApi.define('category',{
name: '',
categoryType: '',
sort: '',
deleted: '',
createdAt: '',
updatedAt: '',
items:{
jsonApi: 'hasMany',
type: 'items'
},
subCategories:{
jsonApi: 'hasMany',
type: 'categories'
},
parent:{
jsonApi: 'hasOne',
type: 'category'
}
})
and when I call below function it returns unexpected results.
this.jsonApi.findAll('category', {include: ['sub-categories']})
[{
id: '1',
type: 'categories',
name: 'Cat1',
categoryType: 0,
sort: 1,
deleted: false,
createdAt: '2017-07-31T10:48:48.000Z',
updatedAt: '2017-07-31T10:48:50.000Z',
subCategories: [],
parent: null,
items: [],
links: { self: 'http://192.168.0.124:3003/api/v1/categories/1' }
},
{
id: '2',
type: 'categories',
name: 'Cat2',
categoryType: 0,
sort: 2,
deleted: false,
createdAt: '2017-07-31T11:21:14.000Z',
updatedAt: '2017-07-31T11:21:15.000Z',
subCategories: [],
parent: null,
items: [],
links: { self: 'http://192.168.0.124:3003/api/v1/categories/2' }
},
{
id: '3',
type: 'categories',
name: 'Sub Cat1',
categoryType: 1,
sort: 1,
deleted: false,
createdAt: '2017-07-31T11:27:21.000Z',
updatedAt: '2017-07-31T11:27:22.000Z',
subCategories: [],
parent: null,
items: [],
links: { self: 'http://192.168.0.124:3003/api/v1/categories/3' }
},
{
id: '4',
type: 'categories',
name: 'Sub Cat2',
categoryType: 1,
sort: 1,
deleted: false,
createdAt: '2017-07-31T11:27:54.000Z',
updatedAt: '2017-07-31T11:27:55.000Z',
subCategories: [],
parent: null,
items: [],
links: { self: 'http://192.168.0.124:3003/api/v1/categories/4' }
}]
as you can see, it doesn't take sub categories from server.
what's missing? and wrong?
Hope your help.
Best Regards.
I think it's because you used camelCase in the API definition when you should be using kebab-case. If you enclode the property key in brackets, that is allowed.
So devour looks for "subCategory" and see there are none, and does not look for "sub-category" as your API is providing it.
this.jsonApi.define('category',{
name: '',
categoryType: '',
sort: '',
deleted: '',
createdAt: '',
updatedAt: '',
items:{
jsonApi: 'hasMany',
type: 'items'
},
'sub-categories':{
jsonApi: 'hasMany',
type: 'categories'
},
parent:{
jsonApi: 'hasOne',
type: 'category'
}
})
I'm having this same issue, but with comments
so camelCase vs kebab-case isn't the issue for me at least.
I get all comments in a flat array, rather than being parent -> child like they should be. I'd be happy to handle re-organising them on my side, but I can't get access to the links between them, so at this point I'm unable to determine on the client what the structure should be.
To prevent the issue that multi-worded attributes are automatically converted to camelCase while relationships are kept in kebab-case, I added the following middleware. Maybe this is helpful for some (@yaodev778, @SirLamer):
const dashToCamelCase = str => {
return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase())
}
const noteRelationshipsInCamelCase = items => {
for (const item of items) {
const convertedRelationships = {}
for (const relationship in item.relationships) {
if (item.relationships.hasOwnProperty(relationship)) {
convertedRelationships[dashToCamelCase(relationship)] = item.relationships[relationship]
}
}
item.relationships = convertedRelationships
}
return items
}
const responseMiddleware = {
name: 'note-relationships-in-camel-case-notation',
res: (payload) => {
payload.res.data.data = noteRelationshipsInCamelCase(payload.res.data.data)
return payload
},
}
jsonApi.insertMiddlewareBefore('response', responseMiddleware)
Wouldn't it make sense for devour to do this be default like it does for attributes?