Generate links with additional query parameters using PagedResourcesAssembler - spring-hateoas

I'm using spring-data-common's PagedResourcesAssembler in my REST controller, and I was happy to see it even generates next/previous links in the response. However, in those cases where I have additional query parameters (besides page, size, sort), these are not included in the generated links. Can I somehow configure the assembler to include the parameters in the links?
Many thanks,
Daniel

You need to build base link by yourself and pass it to "toResource" method of PagedResourcesAssembler.
#Controller
#RequestMapping(value = "/offer")
public class OfferController {
private final OfferService offerService;
private final OfferAssembler offerAssembler;
#Autowired
public OfferController(final OfferService offerService, OfferAssembler offerAssembler) {
this.offerService= checkNotNull(offerService);
this.offerAssembler= checkNotNull(offerAssembler);
}
#RequestMapping(value = "/search/findById", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<PagedResources<OfferResource>> findOfferById(
#RequestParam(value = "offerId") long offerId, Pageable pageable,
PagedResourcesAssembler<OfferDetails> pagedResourcesAssembler) {
Page<OfferDetails> page = service.findById(offerId, pageable);
Link link = linkTo(methodOn(OfferController.class).findOfferById(offerId,
pageable,
pagedResourcesAssembler)).withSelfRel();
PagedResources<OfferResource> resource = pagedResourcesAssembler.toResource(page, assembler, link);
return new ResponseEntity<>(resource, HttpStatus.OK);
}
}
As a result you will get:
http://[your host]/[your app context]/offer/search/findById?offerId=[some offer id]{&page,size,sort}

The following solution is based on the answer provided by #palisade, but addresses the problem of pagination parameters not appearing in the self link--a problem noted by two of the answer's commenters, and which I experienced myself.
By replacing palisade's link declaration...
Link link = linkTo(methodOn(OfferController.class).findOfferById(offerId,
pageable,
pagedResourcesAssembler)).withSelfRel();
...with the following...
Link link = new Link(ServletUriComponentsBuilder.fromCurrentRequest().build()
.toUriString())
.withSelfRel();
...I'm getting page links that look like this:
{
"links": [
{
"rel": "first",
"href": "http://[your host]/[your app context]/offer/search/findById?offerId=[some offer id]&page=0&size=1"
},
{
"rel": "prev",
"href": "http://[your host]/[your app context]/offer/search/findById?offerId=[some offer id]&page=2&size=1"
},
{
"rel": "self",
"href": "http://[your host]/[your app context]/offer/search/findById?offerId=[some offer id]&page=3&size=1"
},
{
"rel": "next",
"href": "http://[your host]/[your app context]/offer/search/findById?offerId=[some offer id]&page=4&size=1"
},
{
"rel": "last",
"href": "http://[your host]/[your app context]/offer/search/findById?offerId=[some offer id]&page=6&size=1"
}
],
"content": [
{
...

Related

BinaryFormatter has been deprecated, how to deserialize entire object graph from json

update:
In newtonsoft, it works fine like this.
var saveObject = Newtonsoft.Json.JsonConvert.SerializeObject(root, Newtonsoft.Json.Formatting.Indented);
var root2 = Newtonsoft.Json.JsonConvert.DeserializeObject<Node>(saveObject);
Here, the entire "tree"/graph is reloaded into root2. Is this behaviour accomplishable with System.Text.Json?
original question:
I have a data model (more precisely a DOM) where I have been using BinaryFormatter to serialize and deserialize the entire graph of connected various objects. It has worked flawless for years. Now, the binaryFormatter has been deprecated, and Microsoft appears to suggest that one should use Json for the job.
Using System.Text.Json serializing the dom, the entire graph is serialized easily, however, when deserializing, only the root level node is deserialized. Any objects hanging on the root node is not deserialized. What am I missing to reload the entire graph? Annotations on the list property? Could anyone guide me to a good example? Or, any other good suggestions to save a dom on disc? Thank you.
using System.Text.Json;
var root = new Node("root");
var a1 = new Node("a1");
var a2 = new Node("a2");
var b11 = new Node("b11");
var b12 = new Node("b12");
var b21 = new Node("b21");
var b22 = new Node("b22");
root.Nodes.Add(a1);
root.Nodes.Add(a2);
a1.Nodes.Add(b11);
a1.Nodes.Add(b12);
a2.Nodes.Add(b21);
a2.Nodes.Add(b22);
var options = new JsonSerializerOptions { WriteIndented = true, };
string saveFile = JsonSerializer.Serialize(root,options);
Console.WriteLine("Savefile:");
Console.WriteLine(saveFile);
var loadFile = JsonSerializer.Deserialize<Node>(saveFile,options)!;
Console.WriteLine();
Console.WriteLine("Loadfile:");
Console.WriteLine(loadFile);
public class Node
{
public Node(string name)
{
Name = name;
}
public string Name { get; set; }
public List<Node> Nodes { get; set; } = new List<Node>();
}
Output:
Savefile:
{
"Name": "root",
"Nodes": [
{
"Name": "a1",
"Nodes": [
{
"Name": "b11",
"Nodes": []
},
{
"Name": "b12",
"Nodes": []
}
]
},
{
"Name": "a2",
"Nodes": [
{
"Name": "b21",
"Nodes": []
},
{
"Name": "b22",
"Nodes": []
}
]
}
]
}
Loadfile:
Node

Angular try from local json file find by id

I have local json file (Kitchen types), i create KitchenTypesService there are 2 inside function GET AND FIND(ID), GET function its work, but not working find function, have error "ERROR TypeError: Unable to lift unknown Observable type", i try with find function get kitchen with id. Tell me what's the problem
Service
export class KitchenTypesService {
private _jsonURL = 'assets/data/kitchenTypes.json';
constructor(private http: HttpClient) {
this.get().subscribe((data) => data);
}
public get(): Observable<any> {
return this.http.get(this._jsonURL);
}
public find(id: number) {
this.get().subscribe(find((data: any) => data.id == id));
}
}
Component
export class KitchenDimensionComponent implements OnInit {
title: string = 'Virtuvės matmenys';
step: number = 2;
selectedKitchenId: number;
kitchen: any;
constructor(
private persistenceService: PersistenceService,
private httpKitchenTypes: KitchenTypesService
) {}
ngOnInit(): void {
this.initialSelectedKitchenId();
console.log(this.httpKitchenTypes.find(1));
}
initialSelectedKitchenId(): void {
this.selectedKitchenId = this.persistenceService.get('selectedKitchenId');
}
}
Local KitcehTypes.json
[
{
"id": 1,
"title": "Standartine",
"src": "/assets/images/kitchen-types/one-well.png",
},
{
"id": 2,
"title": "L forma",
"src": "/assets/images/kitchen-types/L-shaped.png",
},
{
"id": 3,
"title": "U forma",
"src": "/assets/images/kitchen-types/U-shaped.png",
},
{
"id": 4,
"title": "G forma",
"src": "/assets/images/kitchen-types/G-shaped.png",
}
]
Error Message
[
So there are a few ways to tackle this. You're right, HttpClient does cause the error you mentioned, but there is a way around this. May be this can help you.
Directly importing the JSON file using resolveJsonModuleTo work with this, you'll need to add the following in your tsconfig.json file
"resolveJsonModule": true Then you can simply import your data by adding this in your service:
import * as data from './kitchenTypes.json'
Note that your will need to update the file path accordingly
Once this is done, you can now access the JSON file data. You can view, and search the contents of the JSON file as per your need.
data: any = (data as any).default;

How to delete specific parameter using their id in Rest Assured?

Below is the json response
{
"details": [
{
"UserName": "john",
"id": "abc_123",
"LastName": "smith"
}
]
}
I need to delete only the UserName parameter :
request.delete("http://localhost:8080/details/id/UserName");
The above code does not seem to work and my expected is as below
{
"details": [
{
"id": "abc_123",
"LastName": "smith"
}
]
}
Please check for the Minimal, Complete, and Verifiable example before posting a question on SO, There are people to help but we would need to know what you've tried beforehand.
To answer your question, You should use a PUT and not a DELETE cause you are trying to update the payload. DELETE as the name suggests will delete the complete resource
Check this link for more detail
PUT calls are resource specific so you will have to mention which entity should be affected.
I have come up with a sample code based on the details you've provided
Used HashMap here but you could also post the body as such or use POJO or JSONObject
{
Map < String, Object > map = new HashMap < > ();
map.put("details", Arrays.asList(new HashMap < String, Object > () {
{
put("id", "abc_123");
put("LastName", "smith");
}
}));
RequestSpecification req = RestAssured.given();
req.header("Content-Type", "application/json");
req.body(map).when();
Response resp = req.put("http://localhost:8080/details/id/abc_123");
String body = resp.asString();
System.out.println("Response is : " + body);
}

Function ($ServiceBus) Error: The listener for function 'ServiceBus' was unable to start. Microsoft.ServiceBus: 40400: Endpoint not found

I have having an issue trying to create a servicebus Azure function in Visual Studio. When I execute the function in VS2017 it works perfectly , however when i publish the message I get the exception:
Function ($ServiceBus) Error: The listener for function 'ServiceBus'
was unable to start. Microsoft.ServiceBus: 40400: Endpoint not found.,
Resource:sb://systemmonitordan.servicebus.windows.net/testtopic/subscriptions/catchall.
TrackingId:b1a96c67-032c-4c90-bd10-dbac92d11956_G7,
SystemTracker:systemmonitordan.servicebus.windows.net:testtopic/Subscriptions/CatchAll.
Here is the function
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.ServiceBus.Messaging;
using StackExchange.Redis;
namespace Dan.DataUpdate.ServiceBus
{
public static class ServiceBus
{
private static readonly Lazy<ConnectionMultiplexer> lazyConnection;
static ServiceBus()
{
lazyConnection = new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(System.Environment.GetEnvironmentVariable("redisconectionstring")));
}
[FunctionName("ServiceBus")]
public static void Run([ServiceBusTrigger("testtopic", "CatchAll", AccessRights.Listen, Connection = "ServiceConnections")]string myQueueItem, TraceWriter log)
{
log.Info($"C# ServiceBus queue trigger function processed message: {myQueueItem}");
}
}
}
and here are my local.settings.json settings
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage":"DefaultEndpointsProtocol=https;AccountName=dansystemmonito80dc;AccountKey=accountkeystring;EndpointSuffix=core.windows.net",
"AzureWebJobsDashboard": "",
"ServiceConnections": "Endpoint=sb://systemmonitortest.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=sharedaccesskeystring",
"topicName": "testtopic",
"subscriptionName": "CatchAll"
}
}
here are the function.Json settings
{
"generatedBy": "Microsoft.NET.Sdk.Functions.Generator-1.0.6",
"configurationSource": "attributes",
"bindings": [
{
"type": "serviceBusTrigger",
"connection": "ServiceConnections",
"topicName": "testtopic",
"subscriptionName": "CatchAll",
"accessRights": "listen",
"name": "myQueueItem"
}
],
"disabled": false,
"scriptFile": "../bin/Dan.DataUpdate.ServiceBus.dll",
"entryPoint": "Dan.DataUpdate.ServiceBus.ServiceBus.Run"
}
And Here are my function Application settings
AzureWebJobsDashboard DefaultEndpointsProtocol=https;AccountName=danmonitorfunct895c;AccountKey=accountkey
AzureWebJobsStorage DefaultEndpointsProtocol=https;AccountName=danmonitorfunct895c;AccountKey=accountkey
topicName : testtopic
subscriptionName : CatchAll
ServiceConnections:
Endpoint=sb://systemmonitordan.servicebus.windows.net/
;SharedAccessKeyName=RootManageSharedAccessKey;
SharedAccessKey=sharedAccessKey
Is there something that i am missing?

Simplify looking up nested Json values with Json.NET

I use the Facebook's graph Api to get posts from a Facebook page I administer. To get a url to the full size picture of a post I included the "attachments" field. the JSon obtained is as follows:
{
"data": [
{
"message": "Using Facebook's Graph Api to get Testdrive's news from the Facebook page on to the website. So this post will be visible in a minute at the website as well. Cool!",
"link": "https://www.facebook.com/TestdriveDressage/photos/a.493612667417831.1073741827.493607594085005/681741335271629/?type=1&relevant_count=1",
"picture": "https://scontent-b.xx.fbcdn.net/hphotos-xpf1/v/t1.0-9/s130x130/10394069_681741335271629_2094079936902591674_n.png?oh=85676b5ec301e78bd15e2cabde9b8f8f&oe=5561C419",
"id": "493607594085005_681741408604955",
"created_time": "2015-02-03T15:58:54+0000",
"attachments": {
"data": [
{
"description": "Using Facebook's Graph Api to get Testdrive's news from the Facebook page on to the website. So this post will be visible in a minute at the website as well. Cool!",
"media": {
"image": {
"height": 666,
"src": "https://scontent-b.xx.fbcdn.net/hphotos-xpf1/v/t1.0-9/s720x720/10394069_681741335271629_2094079936902591674_n.png?oh=ac58799007b9b909ebc9f0ca762fd6c6&oe=554BD8A3",
"width": 720
}
},
"target": {
"id": "681741335271629",
"url": "https://www.facebook.com/TestdriveDressage/photos/a.493612667417831.1073741827.493607594085005/681741335271629/?type=1"
},
"title": "Timeline Photos",
"type": "photo",
"url": "https://www.facebook.com/TestdriveDressage/photos/a.493612667417831.1073741827.493607594085005/681741335271629/?type=1"
}
]
}
}, ... next "post"
Now I use Json.Net in c£ like this to get post.data.attachments.media.image.src:
FacebookClient fbClient = new FacebookClient(HttpContext.Current.Session[SessionFacebookAccessToken].ToString());
JObject posts = JObject.Parse(fbClient.Get(String.Format("/{0}/posts?fields=message,picture,link,attachments", FacebookPageId)).ToString());
JArray postItems = (JArray)posts["data"];
List<NewsItem> newsItems = new List<NewsItem>();
NewsItem ni;
foreach (JToken item in postItems.Where(item => item["message"] != null))
{
ni = new NewsItem { Message = item.Value<String>("message"), DateTimeCreation = item.Value<DateTime?>("created_time"), Link = item.Value<String>("link"), Thumbnail = item.Value<String>("picture") };
JToken attachments = item["attachments"];
// "Browse" attachments node for possible links to larger image...
if (attachments != null)
{
JToken attachmentsData = attachments["data"];
if (attachmentsData != null)
{
JToken attachmentsArray = attachments["data"];
if (attachmentsArray != null)
{
JToken media = attachmentsArray[0];
if (media != null)
{
JToken media2 = media["media"];
if (media2 != null)
{
JToken image = media2["image"];
if (image != null)
{
ni.Image = image.Value<String>("src");
}
}
}
}
}
}
newsItems.Add(ni);
}
Is there anyway I can simplify this?
It feels a bit odd and I'm not so happy with it...I tried already item["attachments"]["data"]["media"]["image"]["src"] but doesn't work because there's an array at "data" I guess
Any advice or explanation is appreciated.
Try using SelectToken(). You can specify a path to the value you want. If any item along the path is null, the whole expression will be null. This can greatly simplify your code. I've added a fiddle to demonstrate.
string url = (string)item.SelectToken("attachments.data[0].media.image.src");
Fiddle: https://dotnetfiddle.net/YL6t5c