Google Cloud Endpoints call one endpoint from another - java

Let's say I have the following Google Cloud Endpoints:
#ApiMethod(name = "account.insert")
public Account insertAccount(Account account, #Named("userId") Long userId)
#ApiMethod(name = "user.get")
public User getUser(#Named("id") Long id)
Let's assume Account has a reference to User, which shall be created when the account is inserted. So what I need to do is look up the user by userId first to make sure it exists, then set the reference in Account.
To avoid redundancy, I decided it would be good to call getUser(userId) as this functionality exists as another endpoint.
This would work fine as long as both methods/endpoints are in the same class/file. As soon as I cross the class boundaries, I receive the following message:
"non-static method (...) cannot be referenced from a static content"
First of all, I don't understand why the content is static if it isn't marked as static in the method signature, but maybe the #ApiMethod annotation does its job here, because it is understandable that the API is exposed as a static method.
Second, I would probably be able to get around this by using a delegate function, but then I would like to know if it is considered good practice to call into one of my own endpoints from another endpoint.
Let me know what you think, please.

Related

Rest Resources Separation

I've been trying to start a REST api with Spring Boot and I'm a bit strugling with the separation of my resources and which endpoint should be in which file.
Let's say we have an api enpoint to deal with a user and achievements from this user:
/user/{id} GET - to fetch user by id
/achievement/{id} GET - to fetch by achievement
Which are both in their separates resources file:
UserResource
#RestController
public class UserResource {
public UserResource() {...}
#GetMapping("/users/{id}")
public UserDTO getUser(String id) {
log.debug("REST request to get User : {}", login);
return userService.getUserWithAuthoritiesById(id).map(AdminUserDTO::new));
}
And AchievementResource
#RestController
public class AchievementResource {
public AchievementResource(...) {...}
#GetMapping("/achievements/{id}")
public ResponseEntity<Achievement> getAchievement(#PathVariable Long id) {
return achievementRepository.findById(id);
}
}
So far so good, pretty simple. My problem comes when I must get all achievements from a User. Naming covention says I should have an endpoint such as:
/user/{id}/achievements GET
But where should this endpoint be? I feel like both Resources could be good since for the UserResource, the root of the endpoint is the user, but the AchievementResource could be logical too since we are returning achievements.
Easy answer: you have the wrong problem
But where should this endpoint be?
The definition of the resource should be in your machine readable api definition. You produce the class files you need by feeding your definition into a code generator for your choice of language. The generator will put the classes it creates in files somewhere, and you leave them in this default arrangement until some point in the future when you have a compelling reason to arrange them differently (at which point, you fork the code generator and make your preferred design the default).
That said, when designing by hand there's nothing particularly special about "REST endpoints". The guidelines for where resource classes belong is no different from any other classes in Java....
That said, I find that the literature around file layout heuristics rather disappointing. There doesn't seem to be a lot of material discussing the trade offs of different designs, or contexts in which one choice might be more compelling than another.
For your specific situation, I would advise putting the new resource into a file of its own. The argument here being that your UserResource has User dependencies, and your AchievementsResource has achievements dependencies, but your new thing has both, and as a matter of (hand waves) principle, we should avoid bringing unneeded achievements dependencies into the namespace of the UserResource (and vice versa).
In other words, if we find ourselves adding imports to an existing file to implement a new thing, that's a hint that the new thing may be better placed somewhere else.
Using separate files also has nice mechanical advantages - it reduces merge collisions, each file will have its own source control history (meaning that the history of Users isn't cluttered with a bunch of commits that are exclusively about new thing). See Adam Tornhill's work over at CodeScene, for example.
As you separated the controllers, it is not wrong, you should classify the methods by their general entity, "if I need to recover the user's achievements", it is related to both, however, where does she get this data from? of the Achievements knowing that each achievement must have a relationship in the database with the user, you can very well look it up in the achievement controller with a List returnAchievementsByUser (Integer Id) method.
It depends on your point of view and the business behind the scene. You can use just one endpoint in many cases; if "users" are the main resources who have achievements, then "/users/{user-id}" and {users/{user-id}/achievements/{achievement-id} get the user by Id and special achievement of the user
#RestController
#RequestMapping("users")
public class UsersRestController{
#GetMapping("/{user-id}")
public UserDTO getUser(#PathVariable("user-id") String id) {
code...
}
#GetMapping("/{user-id}/achievements/{achievement-id}")
public AchievementDTO getAchievement(#PathVariable("user-id") String userId,
#PathVariable("achievement-id") String achievementId) {
code...
}
}
And if locating "achievements" on top of "users" in their entity hierarchy has meaning to you and your business, then /achievements/{achievement-id}/users/{user-id} can be a rest presentation:
#RestController
#RequestMapping("achievements")
public class AchievementsRestController{
#GetMapping("/{achievement-id}")
public UserDTO getAchievement(#PathVariable("achievements-id") String id) {
code
}
#GetMapping("/{achievements-id}/users/{user-id}")
public AchievementDTO getAchievement(#PathVariable("user-id") String userId,
#PathVariable("achievement-id") String achievementId) {
code
}
}
finally ,whenever they are not in an entity hierarchy, you can pass userId to
"/achievements/{achievements-id}" (or achievement-id to "/users/{user-id}") as a RequestParam.

How can I pass a customized value to a spring aop advice?

If I take a service method named public void delete(int id); as the pointcut, I want to add an after-returning advice on it, but I don't know what kind of object was deleted(however, the servlet which called the service knows the type value), so I was wondering if I can pass a customized value to this after-returning advice when it is activated, like 'user'. I've already checked the related document on Spring's website and I still know nothing. I'd appreciate your answer, THX.
One solution but its required refactoring in Service method
1) Refactoring
public class DeleteRequest {
String type;
Long id;
}
public boolean delete(DeleteRequest request){ // impl}
2) Use Around Advice
2.1) Before proceeding method execution, read passed parameter & get to be deleted object for "XYZ" requirement.
2.2) Capture result of delete method execution
IF its TRUE then DO your stuff
Note: I used this approach for deleted entity notification. So how we can get deleted entity information in after-advice, hence keep it entity information in before phase & use it in after-successful execution.

Should service layer take objects or id's as input? Should responses be objects?

I have a fairly familiar design where I have a DAO per model, and then a Model service class that uses the DAO and has all the business logic in it.
I'm a bit confused as to how to properly create these service classes, specifically what should my parameters be and my responses be like.
Say I have a JSON action, I could read the JSON, and then load the objects I need and THEN pass those to the service, or I could simply pass in the ID's and then load the objects in the service method.
The confusion is, I could be doing some of these things in my controllers action, or I could be doing it in my service layer. Is there a clear cut answer to these types of questions?
pseudo code below:
UserDao
save
update
delete
getById
UserService
private userDao
GetUser
Update
I will be using my service layer in BOTH the web application and the API layer.
I could have a method like:
def GetSomething(userId: Int, locationId: Int, ...): Something = { ... }
or I could have it like:
def GetSomething(request: GetSomethingRequest): GetSomethingResponse { ... }
Is it one or the other, or should I be mixing both?
First I think using case classes for ids is a good desing so will use UserId(id: Int) and LocationId(id: Int)
Lets say You have a model like this: User(id: UserId, locations: Set[Location])
Then lets say that You want to serve request like /user/1/location/1
Your controller needs to handle this by returning Location json. In order to do this You need to
1. Find User by id (may not exist -> NotFound(user with id))
2. For User find Location (may not exist -> NotFound(location with id)
Taking all this into account, You'll need a service: def find(userId: UserId):Option[User] = {...}. Then in Your User add a function to find a location: def findLocation(id: LocationId): Option[Location]. Putting
findLocation in User prevents so called 'anemic model'
For update things are a bit different. Your Location will probably have fields that are updated automatically (example: version, state etc.). So
you will need another class like eg: LocationData (it can be mapped to form, json etc) and a method def update(data: LocationData): Location on Location which will apply the update.

Dynamic instance-level authorization with Jersey (based on id)

Apologies as I am fairly new to Jersey. I've been trying to find a way to have instance-level access authorization using Jersey resources, but the most granularity I'm seeing is Role or static instance-level permissions. I'm a little puzzled because it
To describe better what I mean: suppose an User owns a group of Post resources - presumably this user has the role Author. I don't want every User who is an Author to be able to modify every Post, though. What is the easiest way to control this?
Is this the kind of authorization that's dealt with within the resource class method? Should I be creating a custom Authorization filter? If so, are there any examples of such a thing out there? I'm a little puzzled as it seems like such a common use case.
Thanks!
The reason there isn't much out there in terms of examples is that it's probably down to your own data model as to how you handle this.
Taking a simple example, if each Post has an owner then your resource would probably look something like this:
#PUT
#Path("{id: [A-Fa-f0-9]+}")
#Consumes(MediaType.APPLICATION_JSON)
public T update(#Context HttpServletRequest request, final T item, #PathParam("id") final String id)
{
final Post post = getPostbyId(id);
if (!post.allowedToUpdate(request.getUserPrincipal())
{
throw new UnauthorizedException();
}
// Authorized, carry on
}
There are no end of variations on this theme, but if you're doing resource-level authorization you probably want to do it in something like this way, where you obtain the resource given its ID and then decide if the user is authorized to carry out the requested operation.

What does `Client.findById(id)` mean?

Looking through the Play documentation for Java I noticed the following block of code:
public static Result show(Long id) {
Client client = Client.findById(id);
return ok(views.html.Client.show(client));
}
Source: http://www.playframework.com/documentation/2.1.0/JavaRouting
I am having some trouble understanding the second line, my understanding of Java Object creation is a typical constructor looks like the following:
Person john = new Person();
What is the second line doing? Creating a Object called client from Class called Client, also what is Client? It doesn't appear to be a part of the Play Framework, certainly I cannot find anything in JavaDocs.
Thanks
Edit:
I found this to be a good point of reference for the answer (http://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html)
Also I think the class Client comes from the following documentation (http://www.playframework.com/documentation/1.1.1/controllers) with Client being just a example model class, the new documentation probably needs updating to clear up this confusion.
Pretty clearly, the class Client has a static function of findById, which takes a Long and returns a Client. Static functions are functions that are defined without any access to object properties, and therefore can be accessed through the class name, rather than having to be accessed through an object. Most likely, the class has a static property containing a collection of all clients in the system by index, and findById grabs an existing Client from that list.
I really have no idea where the class Client is defined, however. I've also made a quick look around for it, and couldn't find it in the obvious places.
There must be a static method called show(Client) on the views.html.Client class that returns some object. That object is passed into an ok(whatever) method, and that ok method returns a Result object.
You're missing some basic knowledge/experience. The sample you gave has nothing to do with routes and in this snippet only first line is important, second is just some hypothetical usage. De facto it could be just...
public static Result show(Long id) {
return ok("You want to display details of client with ID: " + id);
}
Although #BenBarden explained what is that mean correctly , this static method isn't declared anywhere, it's (again) hyphotetical usage of some ORM. For an example the real usage with Ebean's model will be:
Client = Client.find.byId(id);
Of course you can also declare own method in your Client model and name it the same as in the sample, however it will be just only wrapper:
public static Finder<Long, Client> find
= new Finder<>(Long.class, Client.class);
public Client findById(Long id) {
return find.byId(id);
}
Conclusions
You need to examine some samples available with your Play sources to get familiar with some basic syntax, fortunately you'll find it easy.
DO NOT MIX documentation from Play 1.x with Play 2.x they are not compatible!

Categories