Spring HATEOAS template link expansion - spring-hateoas

Using the HATEOAS links functionality which is great I am trying to output a templated url to highlight the filter params available to a user
Example controller method
#RequestMapping(value = "/persons", method = RequestMethod.GET, produces = "application/hal+json")
public PersonsResource getPersons (#RequestParam(required = false, value = "name") String name, #RequestParam(required = false, value = "age") Integer age) {
...
personsResource.add(ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(PersonController.class).getPersons(name, age)).withSelfRel());
}
When this method is invoked with no parameters links appears
_links: {
self: {
href: "http://myserver:8080/persons"
}
}
But I'd like
href: "http://myserver:8080/persons?name={name}&age={age}
Even better if one param was supplied then
href: "http://myserver:8080/persons?name={name}&age=21
Icing on the cake would be query parameters of {...] to be ignored ?
Does anyone know if this is possible using the Spring HATEOAS api ? I have managed to code around this but it seems like a reasonable suggestion for the API ?

You could try AffordanceBuilder from spring-hateoas-ext as a drop-in replacement for ControllerLinkBuilder. It creates template variables for parameters you leave undefined in the linkTo-methodOn idiom.
It not only allows to create templates, but also gives you the full capabilities of a RFC 5988 Link and has knowledge about request bodies, so that one can render Hydra or Html or Siren Responses with form-style request descriptors from it.
Disclaimer: I'm the author of spring-hateoas-ext.

This has been addressed in the latest spring-hateoas version. You can check the following issue:
https://github.com/spring-projects/spring-hateoas/issues/169
You should be able to get the required templated URL using something like:
resource.add(linkTo(methodOn(Controller.class).method(null)).withSelfRel());

I guess, the framework is still pretty immature.
I have v.0.11.0.RELEASE and have the same issue.
When you don't supply parameter values you don't have template URL as a result of the ControllerLinkBuilder.linkTo(methodOn) invocation. It's just the way you said, base path from the method annotation.
But when you supply parameter values it's exactly like you say:
https://stackoverflow.com/some/service/path?name=SomeName&age=11
(in my case parameters are different, but the effect is the one you see here)
The 'conceptually correct' URL should be
https://stackoverflow.com/some/service/path{?name,age}
But Spring HATEOAS doesn't support this. Unless you want to append it yourself in the code. Which is really undesirable.
I checked the UriBuilder from JavaEE, it works the same way, no templating for query parameters supported.

Related

fat free: how to specify a query string in route

In Fat Free Framework, I am trying to figure out how to specify a query string in the route call or the routes.ini file.
I want to be able to decode a route specified like this
http://example.com/search?category=22&term=wide
Note if possible I'd like to have a varying number of elements in the query string.
Thanks in advance
The framework doesn't allow to route query strings, only paths.
So, either you modify your routes to include the parameters in the URI path:
;routes.ini
GET /search/#category = MyController->searchByCategory
GET /search/#category/#term = MyController->searchByCategoryAndTerm
(which requires a strict order of the parameters)
Or you route everything to one same controller and parse the query string there:
;routes.ini
GET /search = MyController->search
class MyController {
function search(\Base $f3) {
$category=$f3->get('GET.category);
$term=$f3->get('GET.term');
//etc.
}
}
If you're implementing a search form, the second solution looks the most appropriate.

handle a String[] via the PortletPreferences (Liferay6.2)

I have built a MVCPortlet that runs on Liferay 6.2.
It uses a PortletPReferences page that works fine to set/get String preferences parameters via the top right configuration menu.
Now I would need to store there a String[] instead of a regular String.
It seems to be possible as you can store and get some String[] via
portletPreferences.getValues("paramName", StringArrayData);
I want the data to be stored from a form multiline select.
I suppose that I need to call my derived controller (derived from DefaultConfigurationAction) and invoke there portletPreferences.setValues(String, String[]);
If so, in the middle, I will neeed the config jsp to pass the String[] array to the controller via a
request.setAttribute(String, String[]);
Do you think the app can work this way in theory?
If so, here are the problems I encountered when trying to make it work:
For any reason, in my config jsp,
request.setAttribute("paramName", myStringArray);
does not work ->
actionRequest.getAttribute("paramName")
retrieves null in my controller
This is quite a surprise as this usually works.
Maybe the config.jsp works a bit differently than standard jsps?
Then, how can I turn my multiline html select into a String[] attribute?
I had in mind to call a JS function when the form is submitted.
this JS function would generate the StringArray from the select ID (easy)
and then would call the actionURL (more complicated).
Is it possible?
thx in advance.
In your render phase (e.g. in config.jsp) you can't change the state of your portlet - e.g. I wouldn't expect any attributes to persist that are set there. They might survive to the end of the render phase, but not persist to the next action call. From a rendered UI to action they need to be part of a form, not request attributes.
You can store portletpreferences as String[], no problem, see the API for getting and setting them
I think maybe you can use an array in client side, and you can update the javascript array, when user is selecting new values.
So you have the javascript array, then when user click on the action, you can execute the action from javascript also, something like this:
Here "products" is the array with your products.
A.io.request(url, {type: 'POST',
data: {
key: products
},
on: {
success: function(event, id, obj) {
}
}
});
From Action methd you can try to get the parameter with:
ParamUtil.getParameterValues(request,"key");

Meteor.http.call (call URL API)

so I'm trying to make a call to a bible verse API in my Meteor application. I made a template with name="display", with a simple {{checkitout}} in the template.
Then for the template, I tried to make the call in its corresponding helper. It looks like this (in coffeescript, but Javascript readers should understand as well):
#Template.display.helpers
checkitout:->
result = Meteor.http.call("GET","http://labs.bible.org/api/passage=john%203:2&type=json")
console.log(result)
The URL is a JSON of a bible verse, but the problem is, the Meteor.http.call requires a third argument, a "callback" (because this is in the client folder). I read some documentation + examples and have no idea what it means.
Also, if I call it like this, is result exactly the JSON file, or do I need to fit it within a new hash? And what does a callback mean? Can someone give me an example?
As helpers are synchronous and API calls are not, you need to store the call result in a reactive variable and return it from the helper:
verse = "Loading..."
verseLoaded = false
verseDep = new Deps.Dependency()
Template.Display.checkItOut = ->
verseDep.depend()
unless verseLoaded
verseLoaded = true
Meteor.http.get "...", (error, result) ->
verse = "..."
verseDep.changed()
verse
On the client, the callback is required as you said. So this is something you could do to query an API and display the JSON result:
Template.Display.helpers
checkItOut: ->
Meteor.http.get 'https://graph.facebook.com/facebook', (error, result) ->
if not error
console.log result # display the the open graph result
Note 1: To use these functions, you need to add the HTTP package to your project with $ meteor add http. You can find further information in the documentation.
Note 2: In your situation, you cannot make an API call client-side due to the Access-Control-Allow-Origin Policy. So, the solution would be to use a method and make the call server-side.
# Client-side
Template.Display.helpers
checkItOut: ->
Meteor.call 'getBibleText', (error, result) ->
if not error
console.log result
# Server-side (server directory)
Meteor.methods
'getBibleText': ->
result = HTTP.get 'http://labs.bible.org/api/?passage=john%203:2&type=xml'
return result

Dart objects with strong typing from JSON

I'm learning Dart and was reading the article Using Dart with JSON Web Services, which told me that I could get help with type checking when converting my objects to and from JSON. I used their code snippet but ended up with compiler warnings. I found another Stack Overflow question which discussed the same problem, and the answer was to use the #proxy annotation and implement noSuchMethod. Here's my attempt:
abstract class Language {
String language;
List targets;
Map website;
}
#proxy
class LanguageImpl extends JsonObject implements Language {
LanguageImpl();
factory LanguageImpl.fromJsonString(string) {
return new JsonObject.fromJsonString(string, new LanguageImpl());
}
noSuchMethod(i) => super.noSuchMethod(i);
}
I don't know if the noSuchMethod implementation is correct, and #proxy seems redundant now. Regardless, the code doesn't do what I want. If I run
var lang1 = new LanguageImpl.fromJsonString('{"language":"Dart"}');
print(JSON.encode(lang1));
print(lang1.language);
print(lang1.language + "!");
var lang2 = new LanguageImpl.fromJsonString('{"language":13.37000}');
print(JSON.encode(lang2));
print(lang2.language);
print(lang2.language + "!");
I get the output
{"language":"Dart"}
Dart
Dart!
{"language":13.37}
13.37
type 'String' is not a subtype of type 'num' of 'other'.
and then a stacktrace. Hence, although the readability is a little bit better (one of the goals of the article), the strong typing promised by the article doesn't work and the code might or might not crash, depending on the input.
What am I doing wrong?
The article mentions static types in one paragraph but JsonObject has nothing to do with static types.
What you get from JsonObject is that you don't need Map access syntax.
Instead of someMap['language'] = value; you can write someObj.language = value; and you get the fields in the autocomplete list, but Dart is not able to do any type checking neither when you assign a value to a field of the object (someObj.language = value;) nor when you use fromJsonString() (as mentioned because of noSuchMethod/#proxy).
I assume that you want an exception to be thrown on this line:
var lang2 = new LanguageImpl.fromJsonString('{"language":13.37000}');
because 13.37 is not a String. In order for JsonObject to do this it would need to use mirrors to determine the type of the field and manually do a type check. This is possible, but it would add to the dart2js output size.
So barring that, I think that throwing a type error when reading the field is reasonable, and you might have just found a bug-worthy issue here. Since noSuchMethod is being used to implement an abstract method, the runtime can actually do a type check on the arguments and return values. It appears from your example that it's not. Care to file a bug?
If this was addressed, then JsonObject could immediate read a field after setting it to cause a type check when decoding without mirrors, and it could do that check in an assert() so that it's only done in checked mode. I think that would be a nice solution.

REST: Updating multiple records

I need to update multiple records using a single HTTP request. An example is selecting a list of emails and marking them as 'Unread'. What is the best (Restful) way to achieve this?
The way I doing right now is, by using a sub resource action
PUT http://example.com/api/emails/mark-as-unread
(in the body)
{ids:[1,2,3....]}
I read this site - http://restful-api-design.readthedocs.io/en/latest/methods.html#actions - and it suggests to use an "actions" sub-collection. e.g.
POST http://example.com/api/emails/actions
(in the body)
{"type":"mark-as-unread", "ids":[1,2,3....]}
Quotes from the referenced webpage:
Sometimes, it is required to expose an operation in the API that inherently is non RESTful. One example of such an operation is where you want to introduce a state change for a resource, but there are multiple ways in which the same final state can be achieved, ... A great example of this is the difference between a “power off” and a “shutdown” of a virtual machine.
As a solution to such non-RESTful operations, an “actions” sub-collection can be used on a resource. Actions are basically RPC-like messages to a resource to perform a certain operation. The “actions” sub-collection can be seen as a command queue to which new action can be POSTed, that are then executed by the API. ...
It should be noted that actions should only be used as an exception, when there’s a good reason that an operation cannot be mapped to one of the standard RESTful methods. ...
Create an algorithm-endpoint, like
http://example.com/api/emails/mark-unread
bulk-update is an algorithm name, a noun. It gets to be the endpoint name in REST, the list of ids are arguments to this algorithm. Typically people send them as URL query arguments in the POST call like
http://example.com/api/emails/mark-unread?ids=1,2,3,4
This is very safe, as POST is non-idempotent and you need not care about any side effects. You might decide differently and if your bulk update carries entire state of such objects opt for PUT
http://example.com/api/emails/bulk-change-state
then you would have to put the actual state into the body of the http call.
I'd prefer a bunch of simple algo like mark-unread?ids=1,2,3,4 rather than one monolithic PUT as it helps with debugging, transparent in logs etc
It a bit complicated to get array of models into an action method as argument. The easiest approach is to form a json string from your client and POST all that to the server (to your action mehtod). You can adopt the following approach
Say your email model is like this:
public class Email
{
public int EmailID {get; set;}
public int StatusID {get; set;}
// more properties
}
So your action method will take the form:
public bool UpdateAll(string EmailsJson)
{
Email[] emails = JsonConvert.DeserializeObject<Emails[]>(EmailsJson);
foreach(Email eml in emails)
{
//do update logic
}
}
Using Json.NET to help with the serialization.
On the client you can write the ajax call as follows:
$.ajax({
url: 'api/emailsvc/updateall',
method: 'post',
data: {
EmailsJson: JSON.stringify([{
ID: 1,
StatusID:2,
//...more json object properties.
},
// more json objects
])
},
success:function(result){
if(result)
alert('updated successfully');
});