0

i have a requirement where i need to have a GET endpoint in my microservice that returns an io.swagger.v3.oas.models.OpenAPI document, and i am wondering how to compose that object. The object in raw form looks like this:

{
"openapi": "3.0.1",
"info": {
"title": "MY API",
"description": "API for accessing stuff and other stuff.",
"termsOfService": "http://website.com",
"contact": {
  "name": "Some chap",
  "url": "https://website.com/s/url",
  "email": "alwaysReplyAll@office.com"
},
"version": "1.0"
},
"paths": {
"/v1/user/{id}/properties": {
  "get": { ...etc etc

ive tried this but the document is just coming up null/blank:

@GetMapping("/openapi3")
public @ResponseBody OpenAPI swag() {
     OpenAPI swagDoc = new OpenAPI();
     GenericOpenApiContextBuilder builder = new GenericOpenApiContextBuilder();

    try {
        swagDoc = builder.buildContext(true).read();
    } catch (OpenApiConfigurationException e) {
        // handle error        
}
    return swagDoc;
}

i have read about springfox but the examples in their docs arent very clear ... and im wondering if that is even necessary. what am i not doing right with this builder?

using Gradle btw

heug
  • 982
  • 2
  • 11
  • 23
  • 1
    Don't swallow exceptions like this; in Spring controllers, if you can't do something interesting with the exception, then just let it escape the controller method and Spring will handle returning an error response. I suspect you're _getting_ some sort of exception, ignoring it, and returning a blank `OpenAPI` instance. – chrylis -cautiouslyoptimistic- Mar 26 '20 at 21:25
  • @chrylis-onstrike- Any enterprise secops will spank you HARD for doing that. – SledgeHammer Mar 26 '20 at 21:26
  • Are you using Springfox or OpenAPI? Are you trying to return the OpenAPI of your actual service? That's already built in... – SledgeHammer Mar 26 '20 at 21:27
  • @SledgeHammer That's what `@ExceptionHandler` is for. Silently returning OK and not logging exceptions to the system log is far worse for data integrity than even the unmasked exception would be for confidentiality. – chrylis -cautiouslyoptimistic- Mar 26 '20 at 21:29
  • this is just placeholder code @chrylis-onstrike- ... when i stepped through this code, it doesnt go into the catch block at all, so, not sure whats going on – heug Mar 26 '20 at 21:29
  • @SledgeHammer not using springfox, yes im trying to return the OpenApi doc for my service – heug Mar 26 '20 at 21:30
  • @heug That functionality is already built in. You don't need to build anything. Your service automatically exposes the doc. In Springfox, its /v2/api-docs?group=xxx... if you are using SpringDoc, its http://server:port/context-path/v3/api-docs. – SledgeHammer Mar 26 '20 at 21:38
  • @heug -- if you are trying to build an OpenAPI doc WITHOUT using SpringDoc or something similiar, I'd not recommend that at all. You'll pretty much be re-writing from scratch what they already do... pretty much all you need is to add the starter dependency and slap on some annotations and you're done. You even get a pretty UI test page for free :). – SledgeHammer Mar 26 '20 at 21:44
  • @SledgeHammer i know a little already about whats built in, the thing is, our devops team has a swagger dashboard that relies on hitting a GET endpoint and receiving an OpenAPI doc ... so are you saying i should just call that other endpoint from within the above GET? – heug Mar 26 '20 at 21:45
  • @Heug Yes, all swagger / openapi / etc. libraries expose the api doc as a GET endpoint already. It's part of the requirement. The dashboard would just call http://yourserver.com/yourroot/v3/api-docs and they'll get the JSON back. – SledgeHammer Mar 26 '20 at 21:49
  • @SledgeHammer theyve made it kinda standardized for all apps so it has to be that other endpoint, so i'll basically just be using it as a pass through endpoint, i'll mess around with that. thanks! – heug Mar 26 '20 at 21:50
  • @SledgeHammer do you have any insight on the best way to call that endpoint thats already in your spring app? do you have to use WebClient or is there an easier way since its the same context? – heug Mar 26 '20 at 22:00
  • @heug, I actually had to call the V2 endpoint in my swagger service because I needed to get at the doc to "massage it". You can just use URL, that's the easiest. I'll post my method in the answer. – SledgeHammer Mar 26 '20 at 22:06
  • also @chrylis-onstrike- relax there chief, nobodys getting spanked because an already well-documented microservice's dashboard swagger endpoint swallowed an exception (also, poor assumption that code which is ancillary to the question is in its final form), i know what youre getting at, but thats a pretty overly hardass reaction to this on a couple levels. – heug Mar 28 '20 at 20:38

2 Answers2

0

Per the discussion in comments, you can modify this method, you don't need to use WebClient. I have to get at the doc in my swagger service and I use this code. You wouldn't return an OpenAPI object, you'd just return a string since you already have the raw json.

getV2SwaggerDoc(new URL("..."));

private String getV2SwaggerDoc(URL url) throws IOException {
    HttpURLConnection connection = (HttpURLConnection)url.openConnection();

    connection.setRequestMethod(RequestMethod.GET.toString());

    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));

    StringBuffer stringBuffer = new StringBuffer();

    String line;

    while ((line = reader.readLine()) != null)
        stringBuffer.append(line);

    reader.close();

    connection.disconnect();

    return stringBuffer.toString();
}
SledgeHammer
  • 7,338
  • 6
  • 41
  • 86
  • can the param for new URL(...) be a relative path like /v3/api-docs and it will be able to pick up the base path dynamically? – heug Mar 26 '20 at 22:16
  • @heug In your RestController method you can add a "HttpServletRequest request" param and Spring will inject it. Then you can do string manipulation on the url thats in request.getRequestURL().toString() to massage it to the right URL. The url in request will be the full url of your method no matter where its running. – SledgeHammer Mar 26 '20 at 22:22
  • apologies on the delay, but i am trying this today and it gets hung up on the "BufferedReader reader" line ... when i step through, it seemingly gets lost in the sauce while instantiating the new reader instance, do i need to do this in its own thread or something? oddly, when i go to step into that code, it immediately just sorta hangs/freezes... no console output. super weird. – heug Mar 27 '20 at 16:16
  • i found out in hanging up on connection.getInputStream() ... either an exception is getting swallowed somewhere, or i dont know what, but it wont process that method @SledgeHammer – heug Mar 27 '20 at 16:24
  • @heug does your doc endpoint work in curl or fiddler? if it doesn't work there, it isn't going to work via code. – SledgeHammer Mar 27 '20 at 16:39
0

i was able to simplify the accepted answer by leveraging Spring's RestTemplate instead of java's low level HTTP lib

private String retrieveSwaggerJson(String url) {
    return new RestTemplate()
            .getForEntity(url, String.class)
            .getBody();
}
heug
  • 982
  • 2
  • 11
  • 23