first our scenario:
we have an OSGI environment, where several bundles publish their own rest endpoint, e.g.:
http://localhost:8080/api/cars
http://localhost:8080/api/food
http://localhost:8080/api/toys
This was done using JAXRSServerFactoryBean.create() method, with address being the ones listed above.
Now we need to add a tenant id to the users requests (not user auth, which is different, as users may be part of several tenants). URLs should look like this:
http://localhost:8080/api/tenant/{tenantid}/cars
http://localhost:8080/api/tenant/{tenantid}/food
http://localhost:8080/api/tenant/{tenantid}/toys
I tried two approaches to achieve this now:
Add tenant-id to address of service (http://localhost:8080/api/tenant/{tenantid}) - Result: I could access my service under the given URL, but I couldn't fill any data for tenantid but had to type {tenantid} in the URL, which is not how I need to use it.
Publish all three services under the same URL (http://localhost:8080/api) moving the tenant-part to the #Path annotation of each api class - Result: Exception, that address was already taken by other endpoint
Does anyone have an idea, how this can be done properly? I know that the ServiceBean can take an array of implementors as an argument instead of a single class, but this is not an option, as the bundles load separately and I had some dependency issues, when I tried make this "all in one".
As a sidenote: I know, we could put tenant id in a header, but typically tenant info is somewhere in a URL (host or path) and we wanna go with this "common" style instead of adding a custom header, though implementation of header style would be much easier (already got it to work).
Any ideas would help.
Thanks,
Kay
Try something like:
#Path("/tenants")
public class TenantResource{
#Path("/{tenantId}/cars")
#Get
public List<Car> getTenantCars(#PathParam("tenantId") long tenantId){...}
#Path("/{tenantId}/food")
#Get
public Food getTenantFood(#PathParam("tenantId") long tenantId){...}
#Path("/{tenantId}/toys")
#Get
public List<Toy> getTenantToys(#PathParam("tenantId") long tenantId){...}
}
If you have URLs such as tenants/{tenantid}/cars then this usually means "the cars of the tenant with id = tenantid".
"cars" is a property of the "tenant" resource and thus should be in the same resource.
I think it might be hard to modularize properties of a resource/ object.
But you could consider a "car" resource and query the resource like: /cars?tenantid={tenantid}
#Path("/cars")
public class CarResource{
#Get
public List<Car> getCarsByTenantId(#QueryParam("tenantId") long tenantId){...}
}
or similar.
Related
I would like to use placeholders in a feature file, like this:
Feature: Talk to two servers
Scenario: Forward data from Server A to Server B
Given MongoDb collection "${db1}/foo" contains the following record:
"""
{"key": "value"}
"""
When I send GET "${server1}/data"
When I forward the respone to PUT "${server2}/data"
Then MongoDB collection "${db2}/bar" MUST contain the following record:
"""
{"key": "value"}
"""
The values of ${server1} etc. would depend on the environment in which the test is to be executed (dev, uat, stage, or prod). Therefore, Scenario Outlines are not applicable in this situation.
Is there any standard way of doing this? Ideally there would be something which maintains a Map<String, String> that can be filled in a #Before or so, and runs automatically between Cucumber and the Step Definition so that inside the step definitions no code is needed.
Given the following step definitions
public class MyStepdefs {
#When("^I send GET "(.*)"$)
public void performGET(final String url) {
// …
}
}
And an appropriate setup, when performGET() is called, the placeholder ${server1} in String uri should already be replaced with a lookup of a value in a Map.
Is there a standard way or feature of Cucumber-Java of doing this? I do not mind if this involves dependency injection. If dependency injection is involved, I would prefer Spring, as Spring is already in use for other reasons in my use case.
The simple answer is that you can't.
The solution to your problem is to remove the incidental details from your scenario all together and access specific server information in the step defintions.
The server and database obviously belong together so lets describe them as a single entity, a service.
The details about the rest calls doesn't really help to convey what you're
actually doing. Features don't describe implementation details, they describe behavior.
Testing if records have been inserted into the database is another bad practice and again doesn't describe behavior. You should be able to replace that by an other API call that fetches the data or some other process that proves the other server has received the information. If there are no such means to extract the data available you should create them. If they can't be created you can wonder if the information even needs to be stored (your service would then appear to have the same properties as a black hole :) ).
I would resolve this all by rewriting the story such that:
Feature: Talk to two services
Scenario: Forward foobar data from Service A to Service B
Given "Service A" has key-value information
When I forward the foobar data from "Service A" to "Service B"
Then "Service B" has received the key-value information
Now that we have two entities Service A and Service B you can create a ServiceInformationService to look up information about Service A and B. You can inject this ServiceInformationService into your step definitions.
So when ever you need some information about Service A, you do
Service a = serviceInformationService.lookup("A");
String apiHost = a.getApiHost():
String dbHost = a.getDatabaseHOst():
In the implementation of the Service you look up the property for that service System.getProperty(serviceName + "_" + apiHostKey) and you make sure that your CI sets A_APIHOST and A_DBHOST, B_APIHOST, B_DBHOST, ect.
You can put the name of the collections in a property file that you look up in a similar way as you'd look up the system properties. Though I would avoid direct interaction with the DB if possible.
The feature you are looking for is supported in gherkin with qaf. It supports to use properties defined in properties file using ${prop.key}. In addition it offers strong resource configuration features to work with different environments. It also supports web-services
I'm developing API using Spring Framework and faced a problem that can be solved by simply adding a necessary logic to every place I have it, but I think that there might be an elegant solution to fix it.
I have the following method in my controller:
#GetMapping("/user/{userId}/permissions")
public List<PermissionDto> list(#PathVariable long userId,
#ModelAttribute #Valid PermissionCriteria criteria) {
return permissionService.list(criteria);
}
The thing is that in dto I have a field called userId. It's made not to have a lot of arguments going to the method of the service. But, I want this user id to be set exactly from path since I use the URL that specifies that we are adding permission exactly to specific user resource. It's doable by making addition line that uses setter in the criteria and sets the value of userId. However, now I should never forget to add this line every time I have a case like that. That's why I decided to move it to InitBinder:
#InitBinder(PERMISSIONS_CRITERIA_NAME)
public void permissionsCriteriaInitBinder(WebDataBinder binder) {
PermissionsCriteria criteria = (PermissionsCriteria) binder.getTarget();
Optional.ofNullable(requestHelper.getUserId())
.map(Long::parseLong)
.ifPresent(criteria::setUserId);
}
It works fine. The user ID is set from the path. However, If I specify request parameter and path variable at the same time, even though userId is set from the path in init binder, it's overridden afterwards before it goes to the controller method. So, this one doesn't solve all the issues.
What I want to find, is someplace where the logic can be put to apply to both init binder(I need it for validation) and controller method. Maybe there is a special type of hook or interceptor or at least something to implement to satisfy this conditions?
I am writing a web app with Spring 4.0.
I have written my controllers in what I believe to be the normal way using the #RequestMapping annotation to define the url pattern which the controller handles.
The snippet below illustrates this for a controller which displays a testimonial ...
#Controller
#RequestMapping("/testimonialView")
public class TestimonialRequestController {
#RequestMapping(value="/{testimonialName}", method=RequestMethod.GET)
public ModelAndView testimonialRequest(#PathVariable String testimonialName, ModelAndView modelAndView) throws FileNotFoundException {
Testimonial testimonial;
. . .
}
}
Elsewhere in my application I want to generate a link bar which includes all the testimonials to include in my left hand nav.
At the moment, when I construct the href for the anchor element to go into the link bar, I am hardcoding the url, like this:
String href="/testimonialView/" + testimonialName;
This does not seem right. If later on I want to change the url structure I have to change it in at least two places - possibly more. Once where the incoming URL is matched to the controller, and once to construct the anchor element which a user will click to invoke that URL.
Is there a best practice way of dealing with this problem? It must be a common one. Is it as simple as using Constants to represent the URLs and accessing these from different places? I know my example is simple but I am assuming the problem must exist for much larger web apps with complex URL structure so I want to understand what best practice is.
I hope this isn't a stupid question. I am keen to ensure that I implement best practice right from the beginning. I have looked through Stackoverflow and Google but nothing quite answers this specific question.
Any help gratefully received.
The short answer is that you can't do this dynamically because #RequestMapping puts data into the code at compile time.
However, there are a couple of options that work.
You can define the string constants in a separate class - this will make it easier for you to change the names of URLs
You can explore the request mappings at runtime within Spring, so could have some code that found URLs you'd coded elsewhere - I've done this for identifying when a URL is dynamic content, vs coded content.
My recommendation is
public class URLs {
public static final String TESTIMONIAL_VIEW = "/testimonialView";
}
with
#RequestMapping(URLs.TESTIMONIAL_VIEW)
and
String href= URLs.TESTIMONIAL_VIEW + "/" + testimonialName;
There isn't any better practice for this afaik. Most you can do is, as Ashley said, is to use constants. But as with any other client-server situation such as the Web, if you change the contract (the url in this case) you'll have to do so for both the client (i.e. the links) and the server (the controller mappings).
I would also mention that your controller can be more general, for example have a "Testimonials" controller and "view/{name}" as an action within that controller.
Hope this helps
Is there any baked-in way, or established Tapestry pattern, to decouple the name of a page Class from the URL which renders it?
My specific problem is that I have a page class in an English codebase but I want the URLs to be in another language.
For example, the Hello.java page should be accessible from www.example.com/hola rather than the standard www.example.com/hello - though it's fine if both of these URLs work.
Ideally I want something like an annotation to configure a different URL name in-place for each individual page class.
Off the top of my head I could solve this myself with a map of URLs to page class names and a custom RequestFilter to do the mapping on each request - but I don't want to reinvent the wheel if there's a baked-in way to do this or a better pattern that anyone can suggest?
Tynamo's tapestry-routing could help you. It depends on how do you want to generate the links to www.example.com/hola and www.example.com/hello
The #At annotation only allows one route per page, but you can contribute all the routes you want via your AppModule, like this:
#Primary
#Contribute(RouteProvider.class)
public static void addRoutes(OrderedConfiguration<Route> configuration, ComponentClassResolver componentClassResolver) {
String pageName = componentClassResolver.resolvePageClassNameToPageName(Home.class.getName());
String canonicalized = componentClassResolver.canonicalizePageName(pageName);
configuration.add("home1", new Route("/home1", canonicalized));
configuration.add("home2", new Route("/home2", canonicalized));
configuration.add("home3", new Route("/home3", canonicalized));
configuration.add("home4", new Route("/home4", canonicalized));
configuration.add("hola", new Route("/hola", canonicalized)); // the last one is going to be use by default to create links to the page
}
The routes are ordered and by default the last one is going to be used to generate the links.
Currently there is no way to avoid using the default route to generate the links.
Tapestry has a LinkTransformer but I've always found the API lacking since you don't have access to the default behaviour. Igor has written a blog post about the LinkTransformer API here
I've always found it necessary to decorate the ComponentEventLinkEncoder so that I can access the default behaviour and tweak it. See ModeComponentEventLinkEncoder.java and AppModule.java for an example which tweaks the default behaviour and does some string manipulation on the URL.
Thiago has created a url rewriter api here but I've never used it myself. I'm pretty sure his solution is based on decorating the ComponentEventLinkEncoder for outbound URLs and a RequestFilter for inbound URLs.
I've developed REST services, but now I realized that I'm doing something wrong.
For example, I have a service which retrieves information about a specific device. Each device has an address: sector.room.group.id.
The URI I did for this GET method was: (...)/services_devices/{sector}/{room}/{group}/{id} But now I realized that I should not have used the '/' to separate the device address, right?
How should I pass the address to this method? Using ';' ?
My GET method is:
#GET
#Path("{sector}/{room}/{group}/{id}")
#Produces("application/json")
public String getDeviceName(#PathParam("sector") int sector, #PathParam("room") int room, #PathParam("group") int group, #PathParam("id") int id) throws Exception
{
String name = null;
try {
name = new DevicesManager().getDeviceName(sector, room, group, id);
} catch (Exception e) {
e.printStackTrace();
}
return name;
}
There is a simple way of change this, to have a correct URI? I have this "error" in many methods.
If there is a hierarchy in your resources path variables are appropriate.
It seems in your case there is a hierarchy between devices and address, but first comes the address and after the deviceName. "deviceName" can be considered a one more hierarchy step.
The best way to reflect the above relations would be the following url:
(...)/sector/room/group/id/deviceName
You can then have another attribute of the device mapped like this:
(...)/sector/room/group/id/deviceOwner
The JAX-RS mapping would be:
#GET
#Path("{sector}/{room}/{group}/{id}/deviceName")
#Produces("application/json")
public String getDeviceName(#PathParam ...) {
//impl.
}
And yes, if the deviceName is the only relevant attribute of the resource, then you can leave out "deviceName" and your orignal mapping is correct.
If the resource at /sector/room/group/id has many attributes you should consider returning a composed object for the path:
#GET
#Path("{sector}/{room}/{group}/{id}")
#Produces("application/json")
public Device getDeviceName(#PathParam...) {
}
REST architectural style introduces HATEOAS, which means that client and server are loosely coupled. Simply the client is not aware of how the URLs look like and gets them from previous responses. (it's similar like surfing thru HTML pages). Of course there will be at least one URL, an entry point, that is known to the client. From this point of view, your need to have correct URIs is irrelevant. What's correct URI? The URI is correct when its form is aligned with RFC.
You are probably introducing URL patterns, that are not RESTful, because it implicates tight coupling between client and server (the client must be aware of URL patterns and have ability to construct URLs from them; fill up sector/room/ etc. in your case)
See also this post:
http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
My advice is; don't waste your time on URL patterns, make URLs simple as is possible, flat hierarchy has also many benefits, and follow HATEOAS principle.