Define a "base Url" in a Retrofit Interface - java

I created a Java library for a REST service using Retrofit (https://square.github.io/retrofit/).
I have an Interface which looks like this:
public interface OrganizationUnitsApi {
#GET("organizationUnits")
Call<List<OrganizationUnit>> getOrganizationUnits();
#GET("organizationUnits/{organizationUnitId}")
Call<OrganizationUnit> getOrganizationUnit(#Path("organizationUnitId") String organizationUnitId);
}
As you can see, both times I resolve to the endpoint "organizationUnits", so my requests in the end will be sent to "https://myservice.com/organizationUnits..."
Is there a possibility in Retrofit to like "annotate" a base Url for this Interface?
Maybe something like this?
#Path("organizationUnits")
public interface OrganizationUnitsApi {
#GET("/")
Call<List<OrganizationUnit>> getOrganizationUnits();
#GET("{organizationUnitId}")
Call<OrganizationUnit> getOrganizationUnit(#Path("organizationUnitId") String organizationUnitId);
}

There is no such API at this moment in retrofit similar to what you are exactly trying to do. However, if you must need to do that, you can pass the organizationUnits as part of the baseUrl while building the retorfit object.
So now you are doing something like this
Retrofit.Builder().baseUrl(someUrl)
You can instead do something like the following
Retrofit.Builder().baseUrl(someUrl/organizationUnitId)
But in that case, if you are using the same base url for multiple API interface, you will have to do extra hassle to configure those separately.

Related

Decoupling from 3rd party library

I am using a third party HTTP client to make GET, POST calls. I don't want to tie my code to this library. So I've decided to create an interface called HttpClient and an implementation of it called HttpClientImpl.
One of the methods in the interface is:
Response get(String url);
The Response object being returned from the interface is the object from the third party library. So this technically does not decouple my code from the third party library.
What is the best approach to decouple myself? Should I create my own response object that can wrap the response of the third party library?
This is a classic case of the Mediator design pattern:
The class which uses the HTTP client shouldn't be exposed to neither the HTTP client implementation (which you've already encapsulated) nor its response object.
Using generics here will not prevent the using class from knowing the response class in this case.
As you suggested - have a wrapping response class / have a converter from the 3rd party response to one of your own.
Instead of abstracting the http library, have you considered abstracting the repositories you are accessing through Http? Say for example, you have the restful endpoints for Tweets:
GET https://someapi.com/Tweets
GET https://someapi.com/Tweets/{id}
POST https://someapi.com/Tweets
PUT https://someapi.com/Tweets/{id}
DELETE https://someapi.com/Tweets/{id}
It would make sense to have a TweetRepository class, which can create, read, update and delete tweets. The interface for this class might look something like the following:
public interface TweetRepository {
public List<Tweet> get();
public int add(Tweet tweet);
public void remove(int id);
public Tweet get(int id);
public void update(int id, Tweet tweet);
}
If your controllers use the interface, then you can make your implementation use whatever http library you want without introducing coupling.

how to publish multiple rest endpoints with same base address?

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.

Retrofit 2 without Model Class

i want to ask about Retrofit 2.0
all this time, i knew Retrofit only with GSON Converter and get the object.
But i dont know how to get the data with API like this
https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty
i'm trying to display all top stories and get the object after i load all the top stories first.
i only know with old school style like this: http://pastebin.com/JMpwjH8H
I'm pretty sure for your example you can just set the response type as a list of Integers like this:
public interface ApiInterface {
#GET("topstories.json?print=pretty")
Call<List<Integer>> getTopStories();
}
Using a POJO would be too complex for what is essentially, just an array of Integers.
First of all, you should know whether it is POST web service or GET web service.
what parameters you will be giving to get the desired response and how would you store the response in POJO. This tutorial will help you with all basic thing that you require for integrating web services

How to implement Search operation for an Observation Resource Provider for Hapi Fhir server?

I am trying to understand how the RESTful Server in Hapi Fhir works and I wanted to implement some #Search methods for Observation resources.
Currently, I have this #Read operation, which successfully works when trying to access the resource (like this: http://localhost:8080/NewFHIRServer/fhir) from the browser:
#Read()
public Observation readObservationById(#IdParam IdDt theId) {
for (Entry<Long, Deque<Observation>> entry : myPatientIdToObservations.entrySet())
{
for (Observation obs : entry.getValue()) {
if (obs.getId().equals(theId)) {
return obs;
}
}
}
throw new ResourceNotFoundException(theId);
}
However, when I try to do something similar for the #Search operation, I am getting errors. I would like to be able to get the response by running the search like this (or similar):
Bundle response = client
.search()
.forResource(Observation.class)
.where(Observation.SUBJECT.hasId("Patient/1"))
.execute();
What parameters do I need to have in my #Read method in order to make this possible? The error I am getting right now is the following:
The FHIR endpoint on this server does not know how to handle GET
operation[Observation] with parameters [[subject]]
and it is obvious why it doesn't work, because my header looks like this:
public Observation searchObservationById(#IdParam IdDt theId)
I have been looking at examples to try to figure this out and I don't quite understand what the syntax in this parameter means:
public List<Patient> getPatient(#RequiredParam(name = Patient.SP_FAMILY) StringParam theFamilyName)...
How would you make the query in order to use this last example?
Thank you
To implement a search method, you need to use #Search instead of #Read on the method. You then use zero-or-more parameters annotated with #OptionalParam or #RequiredParam.
To make your specific example work, you need a search method which implements the _id search parameter, e.g.
#Search
public List<Patient> getPatient(#RequiredParam(name = Patient.SP_RES_ID) StringParam theId) {
}

How can I configure Square's Retrofit Client to handle a request with a variable number of parameters

I'm building an Android App and am using Square's Retrofit library for short-lived network calls. I'm relatively new to Java and Android. Until now I've constructed requests like so:
#GET("/library.php")
void library(
#Query("one_thing") String oneThing,
#Query("another_thing") String anotherThing,
Callback<Map<String,Object>> callback
);
And called them like so:
service.library(oneThing, anotherThing, callback);
I need to implement a request that accepts a variable number of parameters, not more than 10 or so. It's cumbersome to have to define them individually and pass null or something for the ones that aren't present for a given request. Is there a way to define an interface for a request such that it accepts a variable number or parameters and auto-constructs #Querys for each element in the parameter dictionary/map? Something like this:
#GET("/library.php")
void library(
Map<String,Object> parameters,
Callback<Map<String,Object>> callback
);
service.library(parameters, callback);
Thanks in advance for any tips.
Edit: passing null for params that aren't pertinent to the request wont work in this case. Ideally I'd be able to set/create #Querys based on the parameter dictionary, so that keys wont become a #Query if their value is null.
Edit: I'm specifically looking for a solution that works with GET requests.
You could always try passing the parameters as a HTTP Body instead, such as in this example (note: I'm the author)
But as you suggest, use a Map with your values instead, so this might work for you:
#POST("/library.php")
public void library(#Body Map<String, Object> parameters, Callback<Map<String,Object>> callback);
It's a bit late but I'll post it anyway just in case someone found the same problem on Google.
They've introduced the Annotation #QueryMap
This annotation let you pass and object that implements Map class, is not as nice as Post requests that let's you pass an Object as parameter but it get the works done.
#GET("/library.php")
public void library(#QueryMap Map<String, Object> parameters, Callback<Map<String,Object>> callback);

Categories

Resources