Swagger, Endpoints and Pathparameters - web-services

TLDR: Multiple Pathparameters and Endpoints with Swagger
What i would like to to is some API Endpoint like this:
/foo/{id}/bar
Now afaik foo or the first node in the path is defining the endpoint and also the resource to aqcuire. Therefore a FooApiServiceImpl is generated.
The generated ProjectsApiService stub looks like this atm:
#GET
#Path("/{id}/bars")
...
public Response getBarsByFooId(#ApiParam(value = "The id of the foo with the bars",required=true ) #PathParam("id") String id)
throws NotFoundException {
return delegate.getBarByFooId(id);
}
Now my wished behaviour would be to GET all the Bar resources that are connectted to the Foo with the given {id}. Kinda like the reverse order. Is this somehow possible?
If this is not possible... then would also like to ask, how can i get url nodes (foo, bar) that are not defined as {xxx} paramaters in brackets?
Something like this:
public Response getBarsByFooId(String foo, String id, String bar)
throws NotFoundException {
return delegate.getBarByFooId(id);
}

To get the paths segments, inject a UriInfo in your resource class or method:
#Context
private UriInfo uriInfo;
And then invoke UriInfo.getPathSegments() from it:
List<PathSegment> segments = uriInfo.getPathSegments();
See the UriInfo.getPathSegments() documentation:
Get the path of the current request relative to the base URI as a list of PathSegment. This method is useful when the path needs to be parsed, particularly when matrix parameters may be present in the path. All sequences of escaped octets in path segments and matrix parameter values are decoded, equivalent to getPathSegments(true).

Related

RestEasy : GET request with multiple dynamic arguments

The required URL should be something like this :
http://<host>:<port>/path/item?<arguments>
The arguments key and value supposed to be multiple and dynamic, so I cannot use #BeanParam or #QueryParam. Also I can only call this interface without implementation.
My current code is something like this :
public interface RestService {
#GET
#Path("/path/item")
#Produces(MediaType.APPLICATION_JSON)
public JsonNode method(#QueryParam("params") String params);
}
Example of arguments that I want to pass : brand=myBrand&price=myPrice
Is there any way to do something like this ?
My References :
REST Web Service - Dynamic Query Parameters
Passing indefinite Query Parameters with RESTful URL and reading them in RESTEasy
Use UriInfo.getQueryParameters(), as following:
#GET
#Path("/path/item")
#Produces(MediaType.APPLICATION_JSON)
public JsonNode method(#Context UriInfo uriInfo) {
MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
...
}
It returns a MultivaluedMap. Then just iterate over it.

what is restcall,pathcall,namedcall in lagom.javadsl.api.Descriptor;

I am new to microservices and Lagom framework, in the service api where we make ServiceCalls I do not understand the difference of namedcall, pathcall and restcall. where and when should we use which?
for instance in these calls:
ServiceCall<NotUsed, Cargo, Done> register();
restCall(Method.POST, "/api/registration", register()
ServiceCall<NotUsed, NotUsed, Source<Cargo, ?>> getLiveRegistrations();
pathCall("/api/registration/live", getLiveRegistrations())
ServiceCall<User, NotUsed> createUser();
namedCall("/api/users", this::createUser)
The Lagom Documentation covers it pretty well:
Named
The simplest type of identifier is a name, and by default, that name is set to be the same name as the name of the method on the interface that implements it. A custom name can also be supplied, by passing it to the namedCall method:
default Descriptor descriptor() {
return named("hello").withCalls(
namedCall("hello", this::sayHello)
);
}
In this case, we’ve named it hello, instead of the default of sayHello. When implemented using REST, this will mean this call will have a path of /hello.
Path
The second type of identifier is a path based identifier. This uses a URI path and query string to route calls, and from it dynamic path parameters can optionally be extracted out. They can be configured using the pathCall method.
Dynamic path parameters are extracted from the path by declaring dynamic parts in the path. These are prefixed with a colon, for example, a path of /order/:id has a dynamic part called id. Lagom will extract this parameter from the path, and pass it to the service call method.
ServiceCall<NotUsed, Order> getOrder(long id);
default Descriptor descriptor() {
return named("orders").withCalls(
pathCall("/order/:id", this::getOrder)
);
}
Multiple parameters can of course be extracted out, these will be passed to your service call method in the order they are extracted from the URL:
Rest Call
The final type of identifier is a REST identifier. REST identifiers are designed to be used when creating semantic REST APIs. They use both a path, as with the path based identifier, and a request method, to identify them. They can be configured using the restCall method:
ServiceCall<Item, NotUsed> addItem(long orderId);
ServiceCall<NotUsed, Item> getItem(long orderId, String itemId);
ServiceCall<NotUsed, NotUsed> deleteItem(long orderId, String itemId);
default Descriptor descriptor() {
return named("orders").withCalls(
restCall(Method.POST, "/order/:orderId/item", this::addItem),
restCall(Method.GET, "/order/:orderId/item/:itemId", this::getItem),
restCall(Method.DELETE, "/order/:orderId/item/:itemId", this::deleteItem)
);
}

play framework 2.2.2 routing multiple different query params, Ember - data redirect status

I am using play 2.2.2. I have apis of the form
/users?token=abcd
/users?resetToken=abcd
I have configured my route as follows:
GET /users controllers.X.method(token: String)
GET /users controllers.Y.method(resetToken: String)
Now, when I make a call to the /users?resetToken=tokenvalue, I get the following error
For request 'GET /users?resetToken=tokenvalue' [Missing parameter: token]
I could have solved this by routing both the apis to the same method and then checking the query params inside the method. But I want to route the apis to two different methods because of the access restrictions on each of them. One of the apis can be accessed only after login while the other can be accessed with/without login.
Could you please help me resolve the issue?
(Adding more information:)
I tried the following:
GET /users controllers.A.genericMethod()
GET /usersByToken/:token controllers.X.method(token: String)
GET /usersByResetToken/:token controllers.Y.method(token: String)
In controllers.A,
public static Promise<Result> genericMethod(){
Map<String, String[]> queryParams = Context.current().request().queryString();
String[] tokens = queryParams.get("token");
String[] resetTokens = queryParams.get("resetToken");
if (tokens != null && tokens.length == 1) {
return Promise.pure((Result) redirect(controllers.routes.X.method(tokens[0])));
} else if (resetTokens != null && resetTokens.length == 1) {
return Promise.pure((Result) redirect(controllers.routes.Y.method(resetTokens[0])));
} else {
ObjectNode node = ControllerHelper.error(ResponseStatus.BAD_REQUEST,
"Required params not set!");
return Promise.pure((Result) badRequest(node));
}
}
In controllers.X
#SubjectPresent
public static Promise<Result> method(){
....
}
In controllers.Y
public static Promise<Result> method(){
....
}
This works from the play framework point of view.
But I am calling these apis from ember framework through ember-data. So, if I make a call to it from ember-data, say using
this.store.find('user', {token: "abcd"});
which forms the corresponding api url
/users?token=abcd
I get a response of "303, see other" and the required data is not returned.
You can't declare two routes with the same method and path, fortunately you don't have to, you have two solutions, first is declaring 'Optional parameters' (#see: doc) like
GET /users controllers.X.action(token ?= null, resetToken ?= null)
public static Result action(String token, String resetToken){
if (token==null) debug("No token given...");
// etc
}
Other solution is declaring rout w/out params, as you can still get arguments with DynamicForm
GET /users controllers.X.action()
public static Result action(){
DynamicForm dynamicForm = Form.form().bindFromRequest();
String token = dynamicForm.get("token");
if (token==null) debug("No token given...");
// etc
}
Second approach is better especially if your API has large amount of optional params.
Note: Written from top of my head
I understand that one api is protected, requiring a login, and another does not.
You need to have two separate routes, this is the best way.
GET /users controllers.X.method(token: String)
GET /public/users controllers.Y.method(resetToken: String)
Combining it into one route will not allow you to have separate access restrictions.
You need to break it into two routes, one with a private access and one with a public access.
This will change your apis obviously.

how pass Employee object in restFul Get method

I am passing an Employee Object Form Client in RestFul webservices Jaxrs2/jersy2
#GET
#Path("{empObj}")
#Produces(MediaType.APPLICATION_XML)
public Response readPK(#PathParam("empObj")Employee empObj) {
//do Some Work
System.out.println(empObj.getName());
return Response.status(Response.Status.OK).entity(result).build();
}
how can achive this object using GET method??
thanx in advance
By using #PathParam on a method parameter / class field you're basically telling JAX-RS runtime to inject path segment (usually string) to your (String) parameter. If you're sending an object (Employee) representation directly via your URI (query param, path param) you should also provide ParamConverterProvider. Beware that this is not possible in some situation and it's not a recommended practice. However, if you're sending the object from client to server in message body, simply remove #PathParam and MessageBodyReader will take care of converting input stream to your type:
#GET
#Path("{empObj}")
#Produces(MediaType.APPLICATION_XML)
public Response readPK(Employee empObj) {
//do Some Work
System.out.println(empObj.getName());
return Response.status(Response.Status.OK).entity(result).build();
}

Load 2 different input models in Acceleo

I'd like to load 2 different input models (a .bpel and a .wsdl) in my main template of Acceleo.
I loaded the ecore metamodels for both bpel and wsdl and I'd like to be able to use something like this:
[comment encoding = UTF-8 /]
[module generate('http:///org/eclipse/bpel/model/bpel.ecore','http://www.eclipse.org/wsdl/2003/WSDL')/]
[import org::eclipse::acceleo::module::sample::files::processJavaFile /]
[template public generate(aProcess : Process, aDefinition : Definition)]
[comment #main /]
Process Name : [aProcess.name/]
Def Location : [aDefinition.location/]
[/template]
but when I run the acceleo template I get this error:
An internal error occurred during: "Launching Generate".
Could not find public template generate in module generate.
I think I have to modify the java launcher (generate.java) because right now it can't take 2 models as arguments. Do you know how?
Thanks!
** EDIT from Kellindil suggestions:
Just to know if I understood it right, before I get to modify stuff:
I'm trying to modify the Generate() constructor.
I changed it in:
//MODIFIED CODE
public Generate(URI modelURI, URI modelURI2, File targetFolder,
List<? extends Object> arguments) {
initialize(modelURI, targetFolder, arguments);
}
In the generic case, I can see it calls the AbstractAcceleoGenerator.initialize(URI, File, List>?>), shall I call it twice, once per each model? like:
initialize(modelURI, targetFolder, arguments);
initialize(modelURI2, targetFolder, arguments);
Then, to mimic in my Generate() constructor the code that is in the super-implementation:
//NON MODIFIED ACCELEO CODE
Map<String, String> AbstractAcceleoLauncher.generate(Monitor monitor) {
File target = getTargetFolder();
if (!target.exists() && !target.mkdirs()) {
throw new IOException("target directory " + target + " couldn't be created."); //$NON-NLS-1$ //$NON-NLS-2$
}
AcceleoService service = createAcceleoService();
String[] templateNames = getTemplateNames();
Map<String, String> result = new HashMap<String, String>();
for (int i = 0; i < templateNames.length; i++) {
result.putAll(service.doGenerate(getModule(), templateNames[i], getModel(), getArguments(),
target, monitor));
}
postGenerate(getModule().eResource().getResourceSet());
originalResources.clear();
return result;
}
what shall I do? Shall I try to mimic what this method is doing in my Generate() constructor after the initialize() calls?
What you wish to do is indeed possible with Acceleo, but it is not the "default" case that the generated launcher expects.
You'll have to mark the "generate" method of the generated java class as "#generated NOT" (or remove the "#generated" annotation from its javadoc altogether). In this method, what you need to do is mimic the behavior of the super-implementation (in AbstractAcceleoLauncher) does, loading two models instead of one and passing them on to AcceleoService#doGenerate.
In other words, you will need to look at the API Acceleo provides to generate code, and use it in the way that fits your need. Our generated java launcher and the AcceleoService class are there to provide an example that fits the general use case. Changing the behavior can be done by following these samples.
You should'nt need to modify the Generate.java class. By default, it should allow you to perform the code generation.
You need to create a launch config and provide the right arguments (process and definition) in this launch config, that's all.
I don't understand the 'client.xmi' URI that is the 1st argument of your module. It looks like it is your model file, if so remove it from the arguments, which must only contain your metamodels URIs.