After upgrade SpringBoot from 1.5.4 to 2.1.7, the existing Spring HATEOAS code does no longer generate '_embedded' and '_link' in the response, instead it generates 'embedded' and 'link'. I need help to find the root cause of this, and how to fix it. Thank you.
Here is a sample of response payload:
{
"embedded": {
"orders": [
{
"id": 3172,
"orderNumber": 148700990741,
"status": "FINISHED",
"userId": "ffffffff5e7c13a4da4a9d0132e95f73",
"createdAt": "2020-03-26T02:31:32.263Z",
"updatedAt": "2020-03-26T02:31:38.853Z",
"createdBy": "ffffffff57acb9e5e4b0e06a0a570080",
"addresses": [],
"updatedBy": "ffffffff57acb9e5e4b0e06a0a570080",
"successUrl": "http://example.com/success",
"cancelUrl": "http://example.com/cancel",
"link": {
"self": {
"href": "http://local.dev-openclass.com:8088/v1/orders/3172"
},
"order": {
"href": "http://local.dev-openclass.com:8088/v1/orders/3172"
},
"transactions": {
"href": "http://local.dev-openclass.com:8088/v1/orders/3172/transactions"
}
}
}
]
},
"link": {
"self": {
"href": "http://local.dev-openclass.com:8088/v1/orders/search/?userId=ffffffff5e7c13a4da4a9d0132e95f73"
}
}
}
Related
I'm trying to return a Discovery Response, but the supportedCookingModes only seems to accept standard values and only in the format of ["OFF","BAKE"], not Custom values as indicated by the documentation. Any idea on how to specify custom values?
{
"event": {
"header": {
"namespace": "Alexa.Discovery",
"name": "Discover.Response",
"payloadVersion": "3",
"messageId": "asdf"
},
"payload": {
"endpoints": [
{
"endpointId": "asdf",
"capabilities": [
{
"type": "AlexaInterface",
"interface": "Alexa.Cooking",
"version": "3",
"properties": {
"supported": [
{
"name": "cookingMode"
}
],
"proactivelyReported": true,
"retrievable": true,
"nonControllable": false
},
"configuration": {
"supportsRemoteStart": true,
"supportedCookingModes": [
{
"value": "OFF"
},
{
"value": "BAKE"
},
{
"value": "CUSTOM",
"customName": "FANCY_NANCY_MODE"
}
]
}
}
]
}
]
}
}
}
Custom cooking modes are brand specific. This functionality is not yet publicly available. I recommend you to choose one of the existing cooking modes:
https://developer.amazon.com/en-US/docs/alexa/device-apis/cooking-property-schemas.html#cooking-mode-values
I have created an ARM template in Visual Studio project with connection to slack. When I connect to slack in Azure portal - everything is fine. My slack API connection is authorized and works. My problem is with deployment and setting $connections.
Here is my template
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
<template-params>
},
"variables": {
"slack": "[concat(parameters('appPrefix'), '-slack-', parameters('environment'))]"
},
"resources": [
{
"type": "Microsoft.Web/connections",
"apiVersion": "2016-06-01",
"location": "[resourceGroup().location]",
"name": "[variables('slack')]",
"properties": {
"api": {
"id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/slack')]"
},
"displayName": "Slack",
"parameterValues": {}
}
},
{
"name": "[parameters('logicAppName')]",
"type": "Microsoft.Logic/workflows",
"location": "[parameters('logicAppLocation')]",
"tags": {
"displayName": "LogicApp"
},
"apiVersion": "2016-06-01",
"properties": {
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Collecting_went_wrong": {
"inputs": {
"host": {
"connection": {
"name": "#parameters('$connections')['slack']['connectionId']"
}
},
"method": "post",
"path": "/chat.postMessage",
"queries": {
"channel": "<channel>",
"text": "<message>",
"username": "<user>"
}
},
"runAfter": {},
"type": "ApiConnection"
}
},
"parameters": {
"$connections": {
"type": "object",
"defaultValue": {
}
}
},
"triggers": {
<trigger>
}
},
"parameters": {
"$connections": {
"value": {
"slack": {
"id": "[resourceId('Microsoft.Web/connections', variables('slack'))]",
"connectionId": "[resourceId('Microsoft.Web/connections', variables('slack'))]",
"connectionName": "slack"
}
}
}
}
},
"dependsOn": [
"[resourceId('Microsoft.Web/connections', variables('slack'))]"
]
}
],
"outputs": {}
}
The 'fun' part is validation of template during the deployment:
New-AzureRmResourceGroupDeployment : 17:08:51 - Resource Microsoft.Logic/workflows 'reporting-reminder-logic-app-dev'
failed with message '{
"error": {
"code": "ConnectionsParameterInvalid",
"message": "The provided API connection parameter 'slack' is missing the required property 'id'."
}
}'
I'm really confused here.
What I tried to deploy it as connection and added $connection parameter definition as well, deployment went through. Nevertheless, when I changed connection to $connections in template parameters, Azure portal throw at me the same validation error.
Any idea what am I doing wrong here?
Thanks
The issue was about different ID in resources -> Microsoft/Web.connections -> properties -> id and slack -> connection. Once this connection ids were the same, the validation passed.
So it's only confusing validation message.
When I make a call to a Spring Data Rest Endpoint I am expecting to see the self links and related links within each object. None of the links appear.
RestTemplate Setup:
#HystrixCommand(fallbackMethod = "getFallbackScenicList")
#RequestMapping(value = "/s", method = RequestMethod.GET, produces= MediaType.APPLICATION_JSON_VALUE)
public PagedResources<Scenic> scenic() {
String url = "http://vr-dms-an-scenic/scenic";
ParameterizedTypeReference<PagedResources<Scenic>> ptr = new ParameterizedTypeReference<PagedResources<Scenic>>() {};
ResponseEntity<PagedResources<Scenic>> responseEntity =
this.restTemplate.exchange(url,HttpMethod.GET, null, ptr, 0,100
);
PagedResources<Scenic> resources = responseEntity.getBody();
return resources;
}
Expected Response:
{
"_embedded": {
"scenic": [
{
"id": 1,
"name": "Test1 scenic",
"description": "This is a description1 for displaying information while in development",
"shortDescription": "Short Description Scenic1",
"_links": {
"self": {
"href": "http://localhost:49218/scenic/1"
},
"scenic": {
"href": "http://localhost:49218/scenic/1"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:49218/scenic"
},
"profile": {
"href": "http://localhost:49218/profile/scenic"
},
"search": {
"href": "http://localhost:49218/scenic/search"
}
},
"page": {
"size": 20,
"totalElements": 1,
"totalPages": 1,
"number": 0
}
}
Actual Response:
{
"_embedded": {
"scenic": [
{
"id": 1,
"name": "Test1 scenic",
"description": "This is a description1 for displaying information while in development",
"shortDescription": "Short Description Scenic1"
}
]
},
"_links": {
"self": {
"href": "http://localhost:49218/scenic"
},
"profile": {
"href": "http://localhost:49218/profile/scenic"
},
"search": {
"href": "http://localhost:49218/scenic/search"
}
},
"page": {
"size": 20,
"totalElements": 1,
"totalPages": 1,
"number": 0
}
}
I assume that Scenic doesn't contain links. So instead of
PagedResources<Scenic>
you actually want
PagedResources<Resource<Scenic>>
I have a simple application containing Products, Prices and PricedProducts.
When I request the list of PricedProducts, I want the Price and the Product to be inlined. I marked my Price repository with #RestResource(exported = false), so for this one it works ok. Products, however, need to be self standing entities (I need to be able to build several PricedProducts using the same Product for example).
I created a projection for PricedProduct, added it as excerptProjection, and a GET to /pricedProducts returns:
{
"_embedded": {
"pricedProducts": [
{
"price": {
"value": "100.50",
"currency": "EUR"
},
"product": {
"name": "Poatato",
"description": null,
"pictureUrl": null
},
"_links": {
"self": {
"href": "http://localhost:4200/api/v1.0/pricedProducts/1"
},
"pricedProduct": {
"href": "http://localhost:4200/api/v1.0/pricedProducts/1{?projection}",
"templated": true
},
"product": {
"href": "http://localhost:4200/api/v1.0/pricedProducts/1/product"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:4200/api/v1.0/pricedProducts"
},
"profile": {
"href": "http://localhost:4200/api/v1.0/profile/pricedProducts"
}
}
}
This inlines my product, however it does not supply a self link for it. So in my client app, when somebody edits the name of the product, for example, I don't know which product I must update, unless I do an extra request.
What I did next was to create a projection for Product, which I use inside the projection for PricedProduct. A GET to /pricedProducts now yields:
{
"_embedded": {
"pricedProducts": [
{
"price": {
"value": "100.50",
"currency": "EUR"
},
"product": {
"pictureUrl": null,
"description": null,
"name": "Potato",
"_links": {
"self": {
"href": "http://localhost:4200/api/v1.0/products/1{?projection}",
"templated": true
}
}
},
"_links": {
"self": {
"href": "http://localhost:4200/api/v1.0/pricedProducts/1"
},
"pricedProduct": {
"href": "http://localhost:4200/api/v1.0/pricedProducts/1{?projection}",
"templated": true
},
"product": {
"href": "http://localhost:4200/api/v1.0/pricedProducts/1/product"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:4200/api/v1.0/pricedProducts"
},
"profile": {
"href": "http://localhost:4200/api/v1.0/profile/pricedProducts"
}
}
}
Now my Product has a self link, but it points to its projection (http://localhost:4200/api/v1.0/products/1{?projection}). What I want is:
{
"_embedded": {
"pricedProducts": [
{
"price": {
"value": "100.50",
"currency": "RON"
},
"product": {
"pictureUrl": null,
"description": null,
"name": "Potato",
"_links": {
"self": {
"href": "http://localhost:4200/api/v1.0/products/1
}
}
},
"_links": {
"self": {
"href": "http://localhost:4200/api/v1.0/pricedProducts/1"
},
"pricedProduct": {
"href": "http://localhost:4200/api/v1.0/pricedProducts/1{?projection}",
"templated": true
},
"product": {
"href": "http://localhost:4200/api/v1.0/pricedProducts/1/product"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:4200/api/v1.0/pricedProducts"
},
"profile": {
"href": "http://localhost:4200/api/v1.0/profile/pricedProducts"
}
}
}
Thanks!
I think the simplest, and more correct, thing to do is using a child projection and parse the self link on the client side.
It's a little bit old question, but I faced the same problem today and I found a solution:
All you have to do is to REMOVE the #Projection annotation from the embedded projection (ProductProjection) which is used inside the top level projection (PricedProductProjection).
Of course, in this case you cannot use that projection in GET queries, but I assume you don't even need it. (It was created especially for this purpose)
How do I remove a resource from a object's collection using HATEOAS?
A PUT will set the collection.
A PATCH will allow a partial update/add.
But how do I do partial update/remove?
Do I really need to POST the entire uri-list minus 1 in order to remove a single item?
Take this object as an example:
{
"name": "Bob Test",
"description": "this is the descript",
"_links": {
"self": {
"href": "http://localhost/example/1"
},
"example": {
"href": "http://localhost/example/1"
},
"citations": {
"href": "http://localhost/example/1/citations"
},
}
}
The object has many citations (collection):
{
"_embedded": {
"citations": [
{
"content": "asdfasdf",
"anchor": null,
"_links": {
"self": {
"href": "http://localhost/citations/1"
},
"citation": {
"href": "http://localhost/citations/1"
},
"bioMarker": {
"href": "http://localhost/citations/1/example"
}
}
},
{
"content": "c2",
"anchor": "prf",
"_links": {
"self": {
"href": "http://localhost/citations/2"
},
"citation": {
"href": "http://localhost/citations/2"
},
"bioMarker": {
"href": "http://localhost/citations/2/example"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost/example/1/citations"
}
}
}
Now let's say I want to remove http://localhost/citations/2, how do I remove this particular item from the http://localhost/example/1/citations collection?
Get the content type from the response and send it in request header "Accept" . Usually it will be "application/xxxxx+json"