Swagger not working - java

I'm having a bit of trouble making Swagger display API docs using Restlet. What Swagger shows is just these stuff:
And checking the api-docs it only shows this:
I wonder what is wrong with my code:
public class MyApplication extends SwaggerApplication {
private static final String ROOT_URI = "/";
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach(ROOT_URI, RootServerResource.class);
router.attach(ROOT_URI + "ping", PingServerResource.class);
router.attach(ROOT_URI + "ping/", PingServerResource.class);
// Some code omitted for simplicity
return router;
}
}

You could have a look at this article:
What can APISpark bring to your existing Web APIs (Part 2) -http://restlet.com/blog/2016/01/04/what-can-apispark-bring-to-your-existing-web-apis-part-2/
Both Swagger1 and 2 are supported by the Swagger extension of Restlet:
Swagger v1
public class ContactsApplication extends SwaggerApplication {
public Restlet createInboundRoot() {
Router router = new Router();
(...)
attachSwaggerSpecificationRestlet(router, "/docs");
return router;
}
}
Swagger v2
public class ContactsApplication extends Application {
public Restlet createInboundRoot() {
Router router = new Router();
(...)
Swagger2SpecificationRestlet swagger2SpecificationRestlet
= new Swagger2SpecificationRestlet(this);
swagger2SpecificationRestlet.setBasePath("http://myapp.org/");
swagger2SpecificationRestlet.attach(router, "/docs");
return router;
}
}

The solution is to add this code:
// Configuring Swagger 2 support
Swagger2SpecificationRestlet swagger2SpecificationRestlet
= new Swagger2SpecificationRestlet(this);
swagger2SpecificationRestlet.setBasePath("http://localhost:8080/api-docs");
swagger2SpecificationRestlet.attach(router);
And point the Swagger UI to /swagger.json

Swagger needs to find your API operations. I'm not sure about Restlet, in Jersey you annotate your REST resource classes with #Api and your methods with #ApiOperation. Read more here in the swagger docs.

Related

Feign Registration - Spring Cloud - Change Target without ribbon over-ride

Introduction
I would like to be able to have two different spring profiles, and depending on the profile to change to a hardcoded address for our feign builders.
Currently was have the following:
return builder.target(cls, "http://" + serviceName);
But I would actually like to do the following and over-ride the address:
return builder.target(cls, "http://our-server:8009/" + serviceName);
Why
Sometimes we don't want to run all the services within our development environment. Additionally, some of the services are only available through a zuul gateway sometimes.
So we run the same code in different situations and conditions.
Technical Details
We have the following code that we use for building our Feign Clients.
We had been using the #FeignClient annotation in the past, but lately we decided to start building our feignClients manually.
Example below:
#FeignClient(name = "ab-document-store", configuration = MultiPartSupportConfiguration.class, fallback = DocumentStoreFallback.class)
We call the feignRegistrar class with the following command:
return registerFeignClient(DocumentStoreClient.class, true);
#RequiredArgsConstructor
//#Component
#Slf4j
public class FeignRegistrar {
#Autowired
private Decoder decoder;
#Autowired
private Encoder encoder;
#Autowired
private Client client;
#Autowired
private Contract feignContract;
#Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
#Autowired
private List<RequestInterceptor> interceptors;
public <T> T register(Class<T> cls, String serviceName, boolean isDocumentStore) {
if(isDocumentStore){
encoder = new MultipartFormEncoder(new SpringEncoder(messageConverters));
}
//Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
Feign.Builder builder = Feign.builder()
.client(client)
.encoder(encoder)
.decoder(decoder)
.contract(feignContract)
.logger(new Slf4Logger())
.logLevel(Logger.Level.HEADERS);
builder.requestInterceptor(new RequestInterceptor() {
#Override
public void apply(RequestTemplate template) {
template.header("X-Service-Name", serviceName);
}
});
for(RequestInterceptor interceptor : interceptors) {
builder.requestInterceptor(interceptor);
}
log.debug("Registering {} - as feign proxy ", serviceName);
return builder.target(cls, "http://" + serviceName);
}
public static class Slf4Logger extends Logger {
#Override
protected void log(String configKey, String format, Object... args) {
log.info("{} - {}", configKey, args);
}
}
}
Spring Cloud Property Over-ride
We have also been using property files such as application-ENV.property with entries such as the following:
ab-document-store.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
ab-document-store.ribbon.listOfServers: localhost:8025
Unfortunately, listOfServers is not enough for us. We would like to be able to assign a directory/path as well. Something like:
ab-document-store.ribbon.listOfServers: localhost:8025/ab-document-store
Otherworkaround
I have thought about sneaking in a header into all requests such as X-SERVICE-NAME using a feign interceptor. Then we could point all services to an address (e.g. localhost:9001) , and forward/proxy those requests to localhost:9001/X-SERVICE-NAME.
However, I would prefer a much easier solution such as:
ab-document-store.ribbon.listOfServers: localhost:8025/ab-document-store
But this doesn't work :(
Introduction
I found a solution for this using a proxy that detects a header.
So, I have a feign interceptor on the java-side that attaches a header x-service-name to every feign-request.
I also have a NodeJS proxy, that analyzes requests, finds x-service-name, and re-writes the requests to become: x-service-name/originalRequestPath.
This allows me to have all the microservices behind a zuul gateway but also access them using a eureka-over-ride.
Java-Feign-Interceptor
Feign.Builder builder = Feign.builder()
.client(client)
.encoder(usedEncoder)
.decoder(decoder)
.contract(feignContract)
.logger(new Slf4Logger())
.logLevel(Logger.Level.HEADERS);
builder.requestInterceptor(new RequestInterceptor() {
#Override
public void apply(RequestTemplate template) {
template.header("X-Service-Name", serviceName);
}
});
NodeJS proxy
In the example, my zuul gateway ( or another proxy ) is on localhost:9001.
I'm listening on localhost:1200 .
let enableProxyForJava = process.env.ENABLE_PROXY_FOR_JAVA;
if (enableProxyForJava != undefined && enableProxyForJava.toLowerCase() === 'true') {
var httpProxyJava = require('http-proxy');
var proxyJava = httpProxyJava.createProxy();
gutil.log( gutil.colors.green('Enabling Proxy for Java. Set your Eureka overrides to localhost:1200.') );
require('http').createServer(function(req, res) {
console.log("req.headers['x-service-name'] = " + req.headers['x-service-name']);
console.log("Before req.url:"+ req.url);
if( req.headers['x-service-name'] != undefined){
let change = req.headers['x-service-name'] +req.url;
console.log("After req.url:"+ change);
req.url = change;
}
proxyJava.web(req, res, {
target: 'http://localhost:9001/'
});
}).listen(1200);
}
Property file inside Java Application that has feign clients
mbak-microservice1.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-microservice1.ribbon.listOfServers: localhost:1200
mbak-microservice2.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-microservice2.ribbon.listOfServers: localhost:1200
mbak-document-store.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-document-store.ribbon.listOfServers: localhost:1200

BeanConfig (or similar?) in Swagger 2.0 (OpenApi 3.0)

I am currently migrating our API docs (which were Swagger 1.5) to Swagger 2.0 (OpenApi 3.0)
The API docs are Swagger docs which get generated with java annotations using maven packages swagger-annotations and swagger-jaxrs. I have already updated the pom.xml with new versions so it looks like:
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2</artifactId>
<version>2.0.6</version>
</dependency>
And also all the old annotations are replaced with the new ones (which change quite a lot) and looks fine.
The thing is we were using a BeanConfig to define the docs general config and auto-scan all the REST resources so the documentation got generated automatically at /swagger.json.
The problem is I can't find the "new way" of doing such thing as creating a BeanConfig and auto-scan the resources so everything gets generated at /swagger.json or /openapi.json (maybe now is something like OpenAPIDefinition?)
If somebody could point me to the right direction I would be very grateful...
After some research, I could find some documentation about it in their Github for JAX-RS application, so the result is something similar to what I was doing but now instead of using a BeanConfig, it uses OpenAPI and Info:
#ApplicationPath("/sample")
public class MyApplication extends Application {
public MyApplication(#Context ServletConfig servletConfig) {
super();
OpenAPI oas = new OpenAPI();
Info info = new Info()
.title("Swagger Sample App bootstrap code")
.description("This is a sample server Petstore server. You can find out more about Swagger " +
"at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, " +
"you can use the api key `special-key` to test the authorization filters.")
.termsOfService("http://swagger.io/terms/")
.contact(new Contact()
.email("apiteam#swagger.io"))
.license(new License()
.name("Apache 2.0")
.url("http://www.apache.org/licenses/LICENSE-2.0.html"));
oas.info(info);
SwaggerConfiguration oasConfig = new SwaggerConfiguration()
.openAPI(oas)
.prettyPrint(true)
.resourcePackages(Stream.of("io.swagger.sample.resource").collect(Collectors.toSet()));
try {
new JaxrsOpenApiContextBuilder()
.servletConfig(servletConfig)
.application(this)
.openApiConfiguration(oasConfig)
.buildContext(true);
} catch (OpenApiConfigurationException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
Though OP has answered their own question, but adding a few more details for people like me who landed on this post as I wanted to migrate from swagger 1.x to swagger 2.0 (openAPI 3) and needed complete config.
(This example is for embedded jetty)
// Jetty configuration
// ContextHandlerCollection contexts
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/api");
context.addFilter(GzipFilter.class, "/*", EnumSet.allOf(DispatcherType.class));
ResourceConfig resourceConfig = new ResourceConfig(ImmutableSet.<Class<?>>builder()
.add(MyRestService.class)
.build());
resourceConfig.registerClasses(OpenApiResource.class,AcceptHeaderOpenApiResource.class); // for swagger, this will cerate openapi.json at <host>/api/openapi.json
context.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/*");
contexts.addHandler(context);
If you need to change default swagger config, that can be done by what OP has described in their answer:
OpenAPI oas = new OpenAPI();
Info info = new Info()
.title("Swagger Sample App bootstrap code")
.description("This is a sample server Petstore server. You can find out more about Swagger " +
"at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, " +
"you can use the api key `special-key` to test the authorization filters.")
.termsOfService("http://swagger.io/terms/")
.contact(new Contact()
.email("apiteam#swagger.io"))
.license(new License()
.name("Apache 2.0")
.url("http://www.apache.org/licenses/LICENSE-2.0.html"));
oas.info(info);
SwaggerConfiguration oasConfig = new SwaggerConfiguration()
.openAPI(oas)
.prettyPrint(true)
.resourcePackages(Stream.of("io.swagger.sample.resource").collect(Collectors.toSet()));
try {
new JaxrsOpenApiContextBuilder()
.servletConfig(servletConfig)
.application(this)
.openApiConfiguration(oasConfig)
.buildContext(true);
} catch (OpenApiConfigurationException e) {
throw new RuntimeException(e.getMessage(), e);
}
There is a much simpler solution for the above requirement.
import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.Info;
import org.glassfish.jersey.server.ResourceConfig;
#OpenAPIDefinition(
info =
#Info(
title = "Sample rest service",
version = "1.0.0",
description = "Sample rest service",
contact =
#Contact(
url = "https://jira2.cerner.com/projects/Dey",
name = "ADey")))
public class SampleRestApplication extends ResourceConfig {
public SampleRestApplication() {
register(OpenApiResource.class);
}
}
your service will load your API spec at /openApi.yaml|json.
Another variant on the same theme. You can package up your openAPI config generation logic into a stand-alone class like so:
#Provider
public class SwaggerInfoBlackMagic implements Feature {
#Context ServletConfig config;
#Context Application app;
#Override
public boolean configure(FeatureContext context) {
//The aim here is to force construction of a (convincing) OpenApiContext before swagger does!
//This has been lifted from BaseOpenApiResource
String ctxId = getContextIdFromServletConfig(config);
try {
OpenApiContext ctx = new JaxrsOpenApiContextBuilder()
.servletConfig(config)
.application(app)
//Might need more of these depending on your setup..
//.resourcePackages(resourcePackages)
//.configLocation(configLocation)
.openApiConfiguration(getOpenApi())
.ctxId(ctxId)
.buildContext(true); //this also stores the instance statically
} catch (OpenApiConfigurationException e) {
throw new RuntimeException(e);
}
return true;
}
private OpenAPIConfiguration getOpenApi() {...}
}
Then whenever you need it you can simply add:
jersey().register(SwaggerInfoBlackMagic.class);
It's the same as the above but slightly tidier.

OAuth authentication with DropWizard

I have a set of RESTful web services built on DropWizard. Currently I am using BasicAuth to authenticate the users to use the API .
That involves an overhead of having another DB with user/password details. I was looking about token based authentication and found that DropWizard supports Oauth2 out of the box.
Can anyone help me with a sample implementation of this Oauth2 based authentication ? And what would be the architecture to implement so ?
Any help would be appreciated.
This question has been around for a while, but for future visitors I place an article here which explains how to do it with custom annotations very well:
Implement secure API authentication over HTTP with Dropwizard on Automation Rhapsody
Basically the idea is to implement our own annotations with our own logic (which in this case is using JWT), but the post also points out what custom settings are reqired for Dropwizard.
Even though this question is four years old I wasn't able to find a fully working example of an application that plugs into dropwizard Oauth2 library with your own validation mechanism.
So for the benefit of people who stumble upon this post from google search in future, here is a full working example running on latest dropwizard version 1.3.8
Good luck!
There is an example of OAuth2 authentication in Dropwizard GitHub repo.
Below there is an example for latest version of Dropwizard (v0.7.1):
...
public OAuthFactory(final Authenticator<String, T> authenticator,
final String realm,
final Class<T> generatedClass) {
super(authenticator);
this.required = false;
this.realm = realm;
this.generatedClass = generatedClass;
}
private OAuthFactory(final boolean required,
final Authenticator<String, T> authenticator,
final String realm,
final Class<T> generatedClass) {
super(authenticator);
this.required = required;
this.realm = realm;
this.generatedClass = generatedClass;
}
#Override
public AuthFactory<String, T> clone(boolean required) {
return new OAuthFactory<>(required, authenticator(), this.realm, this.generatedClass);
}
public T provide() {
try {
final String header = request.getHeader(HttpHeaders.AUTHORIZATION);
if (header != null) {
final int space = header.indexOf(' ');
if (space > 0) {
final String method = header.substring(0, space);
if (PREFIX.equalsIgnoreCase(method)) {
final String credentials = header.substring(space + 1);
final Optional<T> result = authenticator().authenticate(credentials);
if (result.isPresent()) {
return result.get();
}
}
}
}
} catch (AuthenticationException e) {
LOGGER.warn("Error authenticating credentials", e);
throw new InternalServerErrorException();
}
if (required) {
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.WWW_AUTHENTICATE, String.format(CHALLENGE_FORMAT, realm))
.type(MediaType.TEXT_PLAIN_TYPE)
.entity("Credentials are required to access this resource.")
.build());
}
return null;
}
#Override
public Class<T> getGeneratedClass() {
return generatedClass;
}
...
Complete code, here!

How to route all path to a single ServerResource with Restlet

This code is a very basic Restlet app:
public class FirstStepsApplication extends Application {
private static final String ROOT_URI = "/";
#Override
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach(ROOT_URI, RootServerResource.class);
return router;
}
}
How can I route not just root "/" into the RootServerResource but also all "/*" path? Is this a limitation of restlet or this can be done?
Try:
router.attachDefault(RootServerResource.class);

Restlet and consuming/storing files

How would one handle consuming an attachment from a client POST/PUT request on the server side and store that file in a local folder, all using Restlet ?
My thoughts are as follows:
Setup Server as follows:
new MailServerComponenet.start();
public MailServer(){
getServers().add(Protocol.HTTP, 8111);
getDefaultHost().attachDefault(new MailServer());
server.getContext().getParameters().set("tracing", "true");
}
#Put
public void store(Form form){
// *And here is where I am not sure*
}
Thanks for any insight and help in advance.
Here are the following steps you should follow to implement your Restlet application:
Create a component
Component component = new Component();
Create an application and attach it on the component
component.getServers().add(Protocol.HTTP, 8182);
Application application = new MyApplication();
component.getDefaultHost().attachDefault(application);
component.start();
Configure the application within the method createInboundRoot (create a router and attach server resource on it)
public class MyApplication extends Application {
#Override
public Restlet createInboundRoot() {
Router router = new Router();
router.attach("/test", MyServerResource.class);
return router;
}
}
Implement the server resources
public class MyServerResource extends ServerResource {
#Post
public Representation handlePost(Representation repr) {
(...)
}
}
Now the global frame is implemented, I would wonder how you would send the content to Restlet. Is it simple binary content within the request payload or multi-part content?
Binary content
#Post
public Representation handlePost(FileRepresentation fileRepr) {
fileRepr.write(new FileOutputStream(
new File("/tmp/myfile.txt")));
return null;
}
Multipart content. You can have a look at this answer in this case: File Upload with Description in Restlet
Hope it helps you,
Thierry

Categories

Resources