spring cloud contract dsl specify path parameter - java

I am trying to create a contract for a GET request and I'd like to use a path parameter, that can be reused in the response as well. Is this at all possible? I can only find examples for POST, query parameters and body's.
So if I want to define a contract that requests an entity i.e. /books/12345-6688, I want to reuse the specified ID in the response.
How do I create a contract for something like this?

Possible since Spring Cloud Contract 1.2.0-RC1 (fixed in this issue).
response {
status 200
body(
path: fromRequest().path(),
pathIndex: fromRequest().path(1) // <-- here
)
}
See the docs.

Nope that's not possible due to https://github.com/tomakehurst/wiremock/issues/383 . Theoretically you could create your own transformer + override the way stubs are generated in Spring Cloud Contract. That way the WireMock stubs would contain a reference to your new transformer (like presented in the WireMock docs - http://wiremock.org/docs/extending-wiremock/). But it sounds like a lot of work for sth that seems not really that necessary. Why do you need to do it like this? On the consumer side you want to test the integration, right? So just hardcode some values in the contract instead of referencing them and just check if you can parse those values.
UPDATE:
If you just need to parametrize the request URL but don't want to reference it in the response you can use regular expressions like here - https://cloud.spring.io/spring-cloud-contract/single/spring-cloud-contract.html#_regular_expressions
UPDATE2:
Like #laffuste has mentioned, starting from RC1 you can reference a concrete path element

Related

Use placeholders in feature files

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

Passing array with in GAE Endpoints

I am just trying out the first example of GAE Endpoints, I modified the sample API Method to resemble this.
#ApiMethod(name = "sayHi")
public MyBean sayHi(#Named("name") String[] names) {
My expectation is to receive a array of strings.
Now when I use the Google API Explorer to test this, [https://apis-explorer.appspot.com/apis-explorer/]
it generates API like this
POST https://myprojectid.appspot.com/_ah/api/myApi/v1/sayHi/arg1/arg2/arg3?fields=data
It eventually returns 404 error. Since the endpoint is not recognized.
What am I doing wrong here? In fact explorer shows name as String not String[]. Any help is appreciated!
First things first: does this work when there is a single String parameter? There's some servlet mapping magic that needs to happen to expose endpoints, and if that is not present in the project, things won't work. See this link to make sure your web.xml is as it should be.
Looking at this link, it seems that if your method parameter is a basic type (not a real Java object), and if it is not specifically included in a #Path annotation, there's some uncertainty in what will happen in your Api:
Path parameters are the method parameters included in the path property of the #ApiMethod annotation. If path is unspecified, any parameters not annotated with #Nullable or #DefaultValue will be automatically added to the path (they will be path parameters).
So it seems that by not including "name" in a #Path annotation, the docs don't state what the format of the path will be. The generated descriptor that the Explorer is looking at seems to think the right answer is /names[0]/names[1]/names[2], kind of like C-style varargs. It might be this disconnect that causes your 404 to happen. Can you try by including "name" in a #Path annotation?
Instead of having an array as a parameter of the endpoint method, you should put an object (java bean) which contains an array as a property.
Then you get the object in your method and you just read the property and treat it as an array.
Edit after some more research, following your comment
Indeed when you try to pass an array as a Path parameter it doesn't work. The different elements of your array are added to the URL (as you show in your question) and it generates a 404 Not Found error. The trick is that you should pass this array as a Query parameter and not a Path Parameter. See this doc: https://cloud.google.com/appengine/docs/java/endpoints/parameter-and-return-types#path_parameters
And indeed, if you do something like that it works very well:
#ApiMethod(name = "sayHi",
path = "sayHiWithName")
public MyBean sayHi(#Named("name") String[] names) {
MyBean response = new MyBean();
response.setData("Hi, " + names[0] + names[1]);
return response;
}
Note that the parameter is NOT added to the path (i.e. we don't have a path like sayHiWithName/{name}).

Best factory pattern for delivering app data object based on application type in http header

I have a business with multiple applications using my webservice resource. I have a web service resource that looks in a http header for the application ID. This tell the server which application is requesting data. My goal is to deliver to my web application developers a method they can call to retrieve all the application specific settings via the application ID.
Given an applicationID i can specify device type, properties file for that app, and whether GCM,APNS or Microsoft Push Notification, etc. So each applicationID has distinct properties basically.
I want the developer to be able to call for this object like this (or similar):
ApplicationData appData = ApplicationDataFactory.getCurrentApplicationData();
and the factory would look something like this:
class ApplicationDataFactory
{
public static ApplicationData getCurrentApplicationData()
{
//notice how im not passing in criteria here, im getting it from the request so call doens't have to know
String criteria = Request.getHTTPHeaderInfo("applicationID");
if ( criteria.equals("Android") )
return new Android();
else if ( criteria.equals("Android-germany") )
return new Android_germany();
else if ( criteria.equals("ios_germany") )
return new ios_germany();
else if ( criteria.equals("ios"))
return new ios();
else if ( criteria.equals("windows") )
return new windows();
return null;//or throw exception
}
}
so Android, ios, and windows objects all extend off ApplicationData class clearly.
So for example the Android.java object would look like this:
class Android extends ApplicationData{
#override
public String getType(){
return "Android"
}
#override
public Properties getProperties{
return system.getProperties("android.properties");
}
}
and the Android-germany and ios-germany will have common data since there both from germany.
First, i dont like that im specifying the criteria inside the factory and also can anyone help me
with a good design pattern i can use to achieve this ? Remember, in the end i want to be able to have the developer call only ApplicationDataFactory.getCurrentApplicationData(); (or something similar) and the correct application info will be sent referenced. I dont have to use a factory here either its just the first thing i thought of.
So your problem is with the fact that the logic for the criteria is within the factory method. Meanwhile, you don't want the user to provide the criteria as an parameter to the factor method.
First of all, I don't like the idea of having a static Request class. A request should be an object that contains information about the current request. I have a suspicion that your code may be prone to race conditions, once you have many concurrent requests (how do you know which request is which?). So as a starting point, I would refactor the Request class so that you work with instances of Request.
I think, the clearest approach would be that you pass in applicationID as a parameter. This makes testability trivial and the code becomes very obvious, too. You take an input and produce the output based on the input. You could pass the Request instead of the applicationID and let the factory handle the retrieval of the applicationID from the request (as you are doing now).
If you think the Request -> applicationID logic should not be part of the factory, you can create another class, such as ApplicationIDResolver which translates a Request to an applicationID. From then on ApplicationDataFactory would be used through an instance and the ApplicationIDResolver would be a constructor parameter. (I think, this is an overkill.). Another option is to add a getApplicationID() method to the Request class.
If you use a dependency injection framework, it may take care of object life cycles/scopes automatically for you, so the ApplicationData could be a request-scoped object and you could tell your dependency injection framework to instantiate ApplicationData objects based on requests and inject them into the classes where they get used.
Better to use for this purposes enum which implements ApplicationData interface and define each entry. You can resolve proper by valueOf() from enum.

Tapestry 5 - decouple page class name from URL

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.

Can we have more than one #Path annotation for same REST method [duplicate]

This question already has answers here:
JAX-RS: Multiple paths
(4 answers)
Closed 2 years ago.
Can we have more than one #Path annotation for same REST method i.e. the method executed is the same, but it is executed on accessing more than one URL?
E.g.: I want to run the searchNames() method on both http://a/b/c and http://a/b.
You can't have mutliple #Path annotations on a single method. It causes a "duplicate annotation" syntax error.
However, there's a number of ways you can effectively map two paths to a method.
Regular expressions in #Path annotation
The #Path annotation in JAX-RS accepts parameters, whose values can be restricted using regular expressions.
This annotation:
#Path("a/{parameter: path1|path2}")
would enable the method to be reached by requests for both /a/path1 and /a/path2. If you need to work with subpaths, escape slashes: {a:path1\\/subPath1|path2\\/subPath2}
Serving responses with a redirection status code
Alternatively, you could set up a redirection. Here's a way to do it in Jersey (the reference implementation of JAX-RS), by defining another subresource. This is just an example, if you prefer a different way of handling redirections, feel free to use it.
#Path("basepath")
public class YourBaseResource {
//this gets injected after the class is instantiated by Jersey
#Context
UriInfo uriInfo;
#Path("a/b")
#GET
public Responce method1(){
return Response.ok("blah blah").build();
}
#Path("a/b/c")
#GET
public Response method2(){
UriBuilder addressBuilder = uriInfo.getBaseUriBuilder();
addressBuilder.path("a/b");
return Response.seeOther(addressBuilder.build()).build();
}
}
Using a servlet filter to rewrite URLs
If you're going to need such functionality often, I suggest intercepting the incoming requests using a servlet filter and rewriting the paths on the fly. This should help you keep all redirections in one place. Ideally, you could use a ready library. UrlRewriteFilter can do the trick, as long as you're fine with a BSD license (check out their google code site for details)
Another option is to handle this with a proxy set up in front of your Java app. You can set up an Apache server to offer basic caching and rewrite rules without complicating your Java code.
As explained in Tom's answer, you can not use more than one #Path annotation on a single method, because you will run into error: duplicate annotation at compile time.
I think the simplest way to get around this is to use method overloading:
#Path("{foo}")
public Response rest(#PathParam("foo") final String foo) {
return this.rest(foo, "");
}
#Path("{foo}/{bar}")
public Response rest(#PathParam("foo") final String foo,
#PathParam("bar") final String bar) {
return Response.ok(foo + " " + bar).build();
}
You could also use more different method names if you run into the case where multiple overloaded methods have the signature.
Another solution for your particular example:
http://a/b/c
http://a/b
Let's suppose that:
/a is for the resource class
/b/c and /b are the paths for the methods
because a full path looks like:
<protocol><host><port><app><url-pattern><resource-path><method-path>.
Use optional parameter
#Path("/b{c : (/c)?}")
public Response searchNames(#PathParam("c") String val) {
...
}
The example above works for all examples like:
/b
/b/
/b/c
/b/c/
but when c is provided, the val is /c (it has a / before).
If you want to fix the problem above (to avoid Java parsing), you need something more complex:
#Path("/b{slash : (/)?}{c:((?<=/).*)?}")
which will return only c (not /c) for the 3rd bullet point, but for the 4th bullet point it will return c/ which has to be parsed in Java.
But for your case ("the method executed is the same"), don't worry about parsing because you don't have different actions.
If you are using Spring then try
#RequestMapping(value = {"/def", "/abc"}, method = RequestMethod.POST)
This will work for both /abc and /def.
– sSaroj Nov 17 '17 at 10:13

Categories

Resources