Spring Data Rest ID conversion using HashIDs - spring-data-rest

We have a concern exposing internal IDs to the outside world. Therefore I'm thinking about using a hashing mechanism (current choice is hashids) to hash our IDs.
I tried to use a #JsonSerializer and #JsonDeserializer mapping on the Entities ID field. But this only takes effect when including the ID in the body, and has no impact on the IDs in the URL paths.
Is there a possibility to do this, e.g. something like an ID Translation SPI?

The only thing I can think of is to create a request filter that would take the request with encoded ID in URL, then decode the ID and redirect to an URL with decoded ID.

What you need is working "right from the box" in Spring Data REST by customizing item resource URIs:
#Configuration
public class RestConfigurer extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.withEntityLookup().forRepository(ModelRepo.class, model -> HashIdUtil.encode(model.getId()), ModelRepo::findByEncodedId);
super.configureRepositoryRestConfiguration(config);
}
}
public interface ModelRepo extends JpaRepository<Model, Long> {
default Model findByEncodedId(String encodedId) {
return getById(HashIdUtil.decode(encodedId));
}
Model getById(Long id);
}
public class HashIdUtil {
private static final Hashids HASHIDS = new Hashids("salt", 8);
public static String encode(Long source) {
return HASHIDS.encode(source);
}
public static Long decode(String source) {
return HASHIDS.decode(source)[0];
}
}
Unfortunately, due to the bug (I suppose), PUT/PATCH-ing entities does not work in Spring Boot 2+, unlike the previous version of SB (1.5+) where it works as expected.
See my demo: sdr-hashids-demo

You could try using a converter.
#Component
#AllArgsConstructor
public class HashIdConverter implements Converter<String, Long> {
private final HashidsUtil hashidsUtil;
#Override
public Long convert(#NonNull String source) {
return hashidsUtil.decodeId(source);
}
}
Using it the way I just showed you is a bit unsafe, but it can do the work quite well if you are careful enough

Related

Cling: Looking for Opensource code for uPnp ContentDirectory

I'm trying to build a uPnP control point for controlling audio and I am using Java the cling library. To browse the music on the server requires the ContentDirectory service, cling provides the api to access this but doesnt provide any classes to represent the various actions and arguments requiring me to write lots of boilerplate code, I wonder does such a library exist ?
For example Ive create a Browse class for the Browse action of a Content Directory
import org.fourthline.cling.model.meta.Action;
import org.fourthline.cling.model.types.UnsignedIntegerFourBytes;
public class Browse extends AbstractActionAndInvocation
{
//INPUT
public static final String OBJECT_ID = "ObjectID";
public static final String BROWSE_FLAG = "BrowseFlag";
public static final String FILTER = "Filter";
public static final String STARTING_INDEX = "StartingIndex";
public static final String REQUESTED_COUNMT = "RequestedCount";
public void setObjectID(String objectID)
{
actionInvocation.setInput(OBJECT_ID, objectID);
}
public void setBrowseFlag(BrowseFlag browseFlag)
{
actionInvocation.setInput(BROWSE_FLAG, browseFlag.getParameterName());
}
public void setFilter(String filter)
{
actionInvocation.setInput(FILTER, filter);
}
public void setStartingIndex(int startingIndex)
{
actionInvocation.setInput(STARTING_INDEX, new UnsignedIntegerFourBytes(startingIndex));
}
public void setRequestedCount(int requestCount)
{
actionInvocation.setInput(REQUESTED_COUNMT, new UnsignedIntegerFourBytes(requestCount));
}
public Browse(Action action)
{
super(action);
}
}
Since ContentDirectory only has a predefined list of Actions it seems weird that these don't already exist somewhere ?
Within the cling-support module there are useful classes such as callback classes for the main services
e.g
org.fourthline.cling.support.contentdirectory.callback.Browse.java;
However I found them to be of limited usefulness and serve more as an example implementation rather than one that can be used as is.

Why do I need to include #JsonProperty with my RestController

Rest Controller:
#RequestMapping(value = "/admin/rest/new-subscriptions")
public List<NewSubscriptionDTO> getNewSubscriptions() {
NewSubscriptionDTO dto = new NewSubscriptionDTO();
dto.setId("54");
dto.setName("John Doe");
return Arrays.asList(dto);
}
NewSubscriptionDTO:
package dermatica.web.admin.rx;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.joda.time.DateTime;
import java.io.Serializable;
public class NewSubscriptionDTO implements Serializable {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I get the following exception:
no properties discovered to create BeanSerializer (to avoid exception,
disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
If I annotate the fields with #JsonProperty it work fine.
Is there a way for the serialization to work automatically without needing this annotation?
#JsonProperty auto-generates a getter/setter that Jackson uses to read/write to the fields during serialization/deserialization. Here are some alternative approaches:
Provide your own public getters/setters for all fields
Make the fields public, generally frowned upon, but if you're creating a simple DTO, that may be acceptable.
Setting ObjectMapper Visibility for FIELD to ANY (see here)
Disable the FAIL_ON_EMPTY_BEANS exception (see here)
Given that your DTO class has getters and setters, this should work without #JsonProperty. I wasn't able to reproduce the exact error message you showed, but here are some suggestions that may help:
[Controller] Explicitly specify the method type as GET, either using method = GET or #GetMapping - not necessary, but it's good to be explicit
[Controller] Make sure you annotate the controller class with #RestController, indicating the response is serialized to JSON and wrapped in an HttpResponse object.
[DTO] You don't need to extend Serializable (see here).
The final controller would look like this:
#RestController
public class MyController {
#GetMapping(value = "/admin/rest/new-subscriptions")
public List<MyDTO> getDTO() {
MyDTO dto = new MyDTO();
dto.setId("54");
dto.setName("John Doe");
return Collections.singletonList(dto);
}
}
Response:
[{"id":"54","name":"John Doe"}]

Invoking custom Jackson deserializers inside of other deserializers

I am writing a Spring Boot app (RESTful webservice) that uses Jackson for serialization. I have the following data models that will be sent back and forth between the service and its HTTP clients (hence these will be serialized/deserialized to/from JSON):
public abstract class BaseEntity {
#JsonIgnore
private Long id;
private UUID refId;
// Getters, setters, ctors, etc.
}
public abstract class BaseLookup extends BaseEntity {
private String name;
private String label;
private String description;
// Getters, setters, ctors, etc.
}
public class State extends BaseLookup {
private String abbrev; // "VT", "FL", etc.
// Getters, setters, ctors, etc.
}
public class Contact extends BaseEntity {
private String givenName;
private String surname;
private State state;
// Getters, setters, ctors, etc.
}
public class Account extends BaseEntity {
private Contact contact;
private String code;
// lots of other fields that will be generated server-side
// Getters, setters, ctors, etc.
}
Thus there will be some endpoints for CRUDding Accounts, others for CRUDding Contacts, etc. For instance, the AccountController will expose endpoints for CRUDding Account instances:
#RestController
#RequestMapping(value = "/accounts")
public class AccountController {
#RequestMapping(method = RequestMethod.POST)
public void createAccount(#RequestBody Account account) {
// Do stuff and persist the account to the DB
}
}
I want to simplify the JSON that HTTP clients must craft in order to create new Account, Contact, etc. instances. At the same time there are fields on those data models that I do not want exposed to the client-side. Things like the BaseEntity#id (which is the PK of the entity in the DB). Or for instance, in the case of State, I just want the client-side to know about (and use) the abbrev field, etc. I don't want them to ever see the other BaseLookup fields or even know about them.
Hence, my end goal is to allow the client to POST the following JSON, and have a custom Jackson deserializer convert that JSON into an Account instance:
{
"contact" : {
"givenName" : "Him",
"surname" : "Himself",
"state" : "NY"
},
"code" : "12345"
}
So you see, like I stated above, this JSON accomplishes several things:
The client-side doesn't provide a BaseEntity#id or BaseEntity#refId when POSTing to create a new instance
For the contact.state field, which is a BaseLookup with several fields (id, refId, name, label, description, abbrev), the user only has to provide the abbrev field, and the deserializer is expected to figure out which State the client is referring to
The Account class actually has many other fields that are inferred/generated server-side; the client doesn't need to know about them in order to create an Account instance
The JSON above is a simplified form of what we would get if we serialized an Account with Jackson's default behavior; this is to make things easier on the client-side and even more secure on the server-side (not exposing PKs, etc.)
The important thing to note here is that the JSON sent to this controller for the contact field is identical to the JSON that will be POSTed to a ContactController for creating new Contact instances.
Here's the problem:
public class AccountDeserializer extends StdDeserializer<Account> {
public AccountDeserializer() {
this(null);
}
public AccountDeserializer(Class<Account> accClazz) {
super(accClazz);
}
#Override
public Account deserialize(JsonParser jsonParser, DeserializationContext dCtx)
throws IOException, JsonProcessingException {
JsonNode jsonNode = jsonParser.codec.readTree(jsonParser)
Contact contact = ??? // TODO: How to invoke ContactDeserializer here?
String accountCode = node.get("code").asText();
// Generate lots of other Account field values here...
Account account = new Account(contact, accountCode, /* other fields here */);
return account;
}
}
Since I will also have a ContactController (for CRUDding Contact instances irrespective of an associated Account), and because I have similar desires to hide Contact fields from the client-side as well as to simplify the JSON coming into this ContactController#createContact endpoint, I will also need a ContactDeserializer in addition to this AccountDeserializer...
public class ContactDeserializer extends StdDeserializer<Contact> {
// ...etc.
}
This ContactDeserializer will be responsible for converting JSON into Contact instances. But since Account instances also contain Contact instances, and because the "contact JSON" inside the outer "account JSON" will be the same as any JSON that the client sends to any of the "contact endpoints", I'd like to invoke the ContactDeserializer from inside the AccountDeserializer somehow.
That way, when the ContactController receives "contact JSON" to create a new Contact instance, the ContactDeserializer is engaged to get the job done. And, if the AccountController receives "account JSON" to create a new Account instance, then the AccountDeserializer is engaged to get that job done...and it uses the ContactDeserialzer to handle the deserialization of the account JSON's internal contact field as well.
Can this be done?! Can one Jackson deserializer reuse other deserializers inside of it? If so, how? If not, then what's the solution here?!
You can invoke ContactDeserializer by calling the treeToValue method of ObjectCodec. Jackson will automatically pick up the ContactDeserializer for you if you've registered it on your ObjectMapper.
public class AccountDeserializer extends JsonDeserializer<Account> {
#Override
public Account deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
JsonNode node = p.readValueAsTree();
JsonNode contactNode = node.get("contact");
Contact contact = null;
if (contactNode != null) {
contact = p.getCodec().treeToValue(contactNode, Contact.class);
}
return new Account(contact, /* account properties */);
}
}
Edit
If you want to add your deserializers to existing mapper which is created by Spring Boot, you can autowire it in one of your configuration classes and customize as you like.
#Configuration
public class ObjectMapperConfiguration {
#Autowired
public void configureObjectMapper(ObjectMapper mapper) {
SimpleModule module = new SimpleModule()
.addDeserializer(Account.class, new AccountDeserializer())
.addDeserializer(Contact.class, new ContactDeserializer());
mapper.registerModule(module);
}
}

Spring & Reslet : is it possible to map a URL path component to a method argument?

I'm new to Restlet, but I've followed the tutorial on Restlet's own website and got a basic application up and running. What I'm doing right now is that I'm setting up a basic ServerResource and expose a #Get method.
What I'd like is to be able to invoke /user/{userId} and get the user representation back. Is it possible, somehow, to hand over the mapping of {userId} to Restlet, which in turn would invoke getUser(String userId) in my ServerResource?
Such feature (binding path variables into annotated method parameters) isn't natively supported in the framework. Such mapping in the annotated method signatures is only supported with input representation.
To get the path variables of a request, you can get them from the request object (method getAttribute), as described below:
public class UserServerResource extends ServerResource {
#Get
public User getUser() {
String userId = getAttribute("userId");
User user = (...)
(...)
return user;
}
}
If you want to share this path variable across several methods, you can define it as a instance variable (notice that a new instance of the server resource is created for each request unlike to Spring REST where each controller is a singleton and such variable must be defined in method signatures). We can leverage the method doInit of the server resource, as described below:
public class UserServerResource extends ServerResource {
private String userId;
private User user;
#Override
protected void doInit() throws ResourceException {
super.doInit();
userId = getAttribute("userId");
// for example
user = loadUser(userId);
// throws a status 404 if user can't be found
setExisting(user != null);
}
#Get
public User getUser() {
return user;
}
#Put
public User saveUser(User user) {
saveUser(user);
return user;
}
#Delete
public void deleteUser() {
deleteUser(user);
}
}
If you really want to use a mapping from request elements (like path variables, ...) to method parameters, you should use JAXRS. Restlet provides a support of this specification. Implementing a similar server resource as above but with JAXRS is described below:
#Path("/users/{userId}")
public class UserResource {
#GET
#Produces("text/xml")
public String getUser(#PathParam("userId") String userId) {
(...)
}
}
For more details, you can have a look at the corresponding documentation: http://restlet.com/technical-resources/restlet-framework/guide/2.2/extensions/jaxrs.
Hop it helps,
Thierry

Ninject, Generic Referential Bindings

I think this falls under the concept of contextual binding, but the Ninject documentation, while very thorough, does not have any examples close enough to my current situation for me to really be certain. I'm still pretty confused.
I basically have classes that represent parameter structures for queries. For instance..
class CurrentUser {
string Email { get; set; }
}
And then an interface that represents its database retrieval (in the data layer)
class CurrentUserQuery : IQueryFor<CurrentUser> {
public CurrentUserQuery(ISession session) {
this.session = session;
}
public Member ExecuteQuery(CurrentUser parameters) {
var member = session.Query<Member>().Where(n => n.Email == CurrentUser.Email);
// validation logic
return member;
}
}
Now then, what I want to do is to establish a simple class that can take a given object and from it get the IQueryFor<T> class, construct it from my Ninject.IKernel (constructor parameter), and perform the ExecuteQuery method on it, passing through the given object.
The only way I have been able to do this was to basically do the following...
Bind<IQueryFor<CurrentUser>>().To<CurrentUserQuery>();
This solves the problem for that one query. But I anticipate there will be a great number of queries... so this method will become not only tedious, but also very prone to redundancy.
I was wondering if there is an inherit way in Ninject to incorporate this kind of behavior.
:-
In the end, my (ideal) way of using this would be ...
class HomeController : Controller {
public HomeController(ITransit transit) {
// injection of the transit service
}
public ActionResult CurrentMember() {
var member = transit.Send(new CurrentUser{ Email = User.Identity.Name });
}
}
Obviously that's not going to work right, since the Send method has no way of knowing the return type.
I've been dissecting Rhino Service Bus extensively and project Alexandria to try and make my light, light, lightweight implementation.
Update
I have been able to get a fairly desired result using .NET 4.0 dynamic objects, such as the following...
dynamic Send<T>(object message);
And then declaring my interface...
public interface IQueryFor<T,K>
{
K Execute(T message);
}
And then its use ...
public class TestCurrentMember
{
public string Email { get; set; }
}
public class TestCurrentMemberQuery : IConsumerFor<TestCurrentMember, Member>
{
private readonly ISession session;
public TestCurrentMemberQuery(ISession session) {
this.session = session;
}
public Member Execute(TestCurrentMember user)
{
// query the session for the current member
var member = session.Query<Member>()
.Where(n => n.Email == user.Email).SingleOrDefault();
return member;
}
}
And then in my Controller...
var member = Transit.Send<TestCurrentMemberQuery>(
new TestCurrentMember {
Email = User.Identity.Name
}
);
effectively using the <T> as my 'Hey, This is what implements the query parameters!'. It does work, but I feel pretty uncomfortable with it. Is this an inappropriate use of the dynamic function of .NET 4.0? Or is this more the reason why it exists in the first place?
Update (2)
For the sake of consistency and keeping this post relative to just the initial question, I'm opening up a different question for the dynamic issue.
Yes, you should be able to handle this with Ninject Conventions. I am just learning the Conventions part of Ninject, and the documentation is sparse; however, the source code for the Conventions extension is quite light and easy to read/navigate, also Remo Gloor is very helpful both here and on the mailing list.
The first thing I would try is a GenericBindingGenerator (changing the filters and scope as needed for your application):
internal class YourModule : NinjectModule
{
public override void Load()
{
Kernel.Scan(a => {
a.From(System.Reflection.Assembly.GetExecutingAssembly());
a.InTransientScope();
a.BindWith(new GenericBindingGenerator(typeof(IQueryFor<>)));
});
}
}
The heart of any BindingGenerator is this interface:
public interface IBindingGenerator
{
void Process(Type type, Func<IContext, object> scopeCallback, IKernel kernel);
}
The Default Binding Generator simply checks if the name of the class matches the name of the interface:
public void Process(Type type, Func<IContext, object> scopeCallback, IKernel kernel)
{
if (!type.IsInterface && !type.IsAbstract)
{
Type service = type.GetInterface("I" + type.Name, false);
if (service != null)
{
kernel.Bind(service).To(type).InScope(scopeCallback);
}
}
}
The GenericBindingGenerator takes a type as a constructor argument, and checks interfaces on classes scanned to see if the Generic definitions of those interfaces match the type passed into the constructor:
public GenericBindingGenerator(Type contractType)
{
if (!contractType.IsGenericType && !contractType.ContainsGenericParameters)
{
throw new ArgumentException("The contract must be an open generic type.", "contractType");
}
this._contractType = contractType;
}
public void Process(Type type, Func<IContext, object> scopeCallback, IKernel kernel)
{
Type service = this.ResolveClosingInterface(type);
if (service != null)
{
kernel.Bind(service).To(type).InScope(scopeCallback);
}
}
public Type ResolveClosingInterface(Type targetType)
{
if (!targetType.IsInterface && !targetType.IsAbstract)
{
do
{
foreach (Type type in targetType.GetInterfaces())
{
if (type.IsGenericType && (type.GetGenericTypeDefinition() == this._contractType))
{
return type;
}
}
targetType = targetType.BaseType;
}
while (targetType != TypeOfObject);
}
return null;
}
So, when the Conventions extension scans the class CurrentUserQuery it will see the interface IQueryFor<CurrentUser>. The generic definition of that interface is IQueryFor<>, so it will match and that type should get registered for that interface.
Lastly, there is a RegexBindingGenerator. It tries to match interfaces of the classes scanned to a Regex given as a constructor argument. If you want to see the details of how that operates, you should be able to peruse the source code for it now.
Also, you should be able to write any implementation of IBindingGenerator that you may need, as the contract is quite simple.