<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: declanTreanor</title>
    <description>The latest articles on Forem by declanTreanor (@declantreanor).</description>
    <link>https://forem.com/declantreanor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F323181%2Faa08551f-c46f-4ae9-a80d-ab9422fedf37.jpeg</url>
      <title>Forem: declanTreanor</title>
      <link>https://forem.com/declantreanor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/declantreanor"/>
    <language>en</language>
    <item>
      <title>How to Consolidate API-Documentation in a Microservices Environment</title>
      <dc:creator>declanTreanor</dc:creator>
      <pubDate>Wed, 18 Mar 2020 09:04:41 +0000</pubDate>
      <link>https://forem.com/eon/how-to-consolidate-api-documentation-in-a-spring-boot-microservices-environment-dgd</link>
      <guid>https://forem.com/eon/how-to-consolidate-api-documentation-in-a-spring-boot-microservices-environment-dgd</guid>
      <description>&lt;p&gt;&lt;em&gt;Consolidate multiple Swagger API documents in a single “source of truth” document to enable developers to utilise APIs more conveniently&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A common problem exists for developers of services, especially in a Microservices environment, and the problem can be described as follows.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;“A problem well stated is a problem half-solved” - Charles Kettering&lt;/p&gt;

&lt;p&gt;Imagine a team of software engineers are working on a group of Microservices. The front-end developers are busy taking care of the aesthetics and usability, from the end-user’s point-of-view, and they need to know how to connect the app with the API in order to satisfy their, and thus the end user’s, requirements.&lt;/p&gt;

&lt;p&gt;What if I have multiple microservices and I want my endpoints to be documented in one place? For example, for the convenience of front-end developers. The third-party documentation implementation in this case is Swagger UI. Most attempts to solve this problem that were found during the literature review, depended on the Swagger-UI-provided facility of the dropdown menu on the swagger-ui.html homepage. That solution meant that a consumer would need to manually access each swagger document in order to locate a suitable service. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fr302hznhi0qsn6tgn57o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fr302hznhi0qsn6tgn57o.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
Fig. 1.1 &lt;em&gt;That label ‘Consolidated’, defaults to ‘Default’; it has been changed inside the source code. It is this dropdown that is used to control which of the APIs to view documentation for, in other solutions for this problem&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Literature Review
&lt;/h2&gt;

&lt;p&gt;Perhaps “Glorified Google” would have been a more accurate title for this section, so with expectations managed, I’ll proceed.&lt;/p&gt;

&lt;p&gt;As mentioned earlier, attempts to solve this problem, encountered in the past, made use of Swagger UI’s facility of the dropdown menu, in the upper-right of the homepage, as illustrated above, for selecting the API, for which to view the documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dzone.com/articles/centralized-documentation-in-microservice-spring-b" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is an example that sees a Eureka server being used to discover each service to be documented. Eureka is a service discovery tool; further information pertaining to Eureka servers can be found &lt;a href="https://www.tutorialspoint.com/spring_boot/spring_boot_eureka_server.htm" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Although elegant, it was felt that it was overkill to have a Eureka instance, solely for this purpose, and a consumer would lack a holistic view of all the services at their disposal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://objectpartners.com/2017/09/28/aggregate-services-into-a-single-swagger" rel="noopener noreferrer"&gt;This is another example&lt;/a&gt; that, although is without a third-party service discovery implementation, leaves us with the necessity to manually select and investigate, each end-point individually.&lt;/p&gt;

&lt;p&gt;In this solution, rather than employing a third-party service discovery tool, the developer simply configured the required end-points. This was an option that had occurred to me and this gave me the confidence to proceed with my intention.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Solution
&lt;/h2&gt;

&lt;p&gt;“The solution often turns out more beautiful than the puzzle.” – Richard Dawkins&lt;/p&gt;

&lt;p&gt;This solution is a Spring-boot app that, while also using Springfox, consolidates all the configured endpoints (inside &lt;code&gt;application.yml&lt;/code&gt;) into one single swagger document. It is relatively straightforward to configure:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;endpoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# a list of sample base urls, note, omit "/v2/api-docs"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UnusedName_key&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://api_server1.eon.de&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;joe_bloggs&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;joe_bloggs_pa55w0rd!&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UnusedName2_key&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://api_server2.eon.de&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;joe_bloggs2&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;joe_bloggs2_pa55w0rd!&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As is standard with Swagger-UI, the documentation process expects Json, describing each endpoint, to be present at the configured urls (+&lt;code&gt;’/v2/api-doc’&lt;/code&gt;). There are two more considerations.&lt;/p&gt;

&lt;p&gt;The following refers to, at least, Swagger version 2.x. It was necessary to hard-code Json as Strings to be available at &lt;code&gt;"/swagger-resources/configuration/ui"&lt;/code&gt; and &lt;code&gt;"/swagger-resources"&lt;/code&gt;. Chrome Development Tools helped me, along with some prompting by a colleague, to come to this realisation. This will be outlined later, in the &lt;em&gt;Configuration&lt;/em&gt; section.&lt;/p&gt;

&lt;p&gt;It was also necessary to have the Json, specific to our APIs, available at &lt;code&gt;‘/v2/api-docs’&lt;/code&gt;, which made it necessary to provide a &lt;code&gt;Controller&lt;/code&gt; with the above &lt;code&gt;RequestMapping&lt;/code&gt; that returned a consolidated &lt;code&gt;Json&lt;/code&gt; representation of all the APIs to be documented, which brings me to the second part of our implementation.&lt;/p&gt;

&lt;p&gt;The consolidation of the swagger &lt;code&gt;Json&lt;/code&gt; is achieved as follows: Google’s GSON is used to generate a &lt;code&gt;Map&lt;/code&gt; “of” the individual &lt;code&gt;Json&lt;/code&gt; representations of each API’s documentation.&lt;/p&gt;

&lt;p&gt;The next step is to merge them into one &lt;code&gt;Map&lt;/code&gt;, containing all the data. &lt;code&gt;Maps&lt;/code&gt;, as you will see, lend themselves very well to merging. The developer can use any means they choose, to arrive at a consolidated &lt;code&gt;Map&lt;/code&gt; representation. This, for example, will do the trick: After creating a variable called &lt;code&gt;original&lt;/code&gt;, which is a &lt;code&gt;Map&lt;/code&gt; that is equal to the first &lt;code&gt;Map&lt;/code&gt;, in the list of &lt;code&gt;Maps&lt;/code&gt; to be merged, that was passed into the deepMerge functionality&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="n"&gt;maps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;updateInfo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;titles&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// this has no effect in the merging process, &lt;/span&gt;
    &lt;span class="c1"&gt;//                          rather it does work relating to the final&lt;/span&gt;
    &lt;span class="c1"&gt;//                          title of the document&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;keySet&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Map&lt;/span&gt; &lt;span class="n"&gt;originalChild&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;Map&lt;/span&gt; &lt;span class="n"&gt;newChild&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deepMerge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;originalChild&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newChild&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;List&lt;/span&gt; &lt;span class="n"&gt;originalChild&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;List&lt;/span&gt; &lt;span class="n"&gt;newChild&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;newChild&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;originalChild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;originalChild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;GSON returns its own &lt;code&gt;Map&lt;/code&gt; implementation to represent Json key-value pairs, this allows recursion to be used, as above, to merge all Maps into one &lt;em&gt;super-map&lt;/em&gt;, which is then converted back, again using Google’s GSON, to Json for display as though it were a single API being rendered by Swagger UI. These will be displayed in alphabetical order, grouped by API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Configuration of this consolidated-swagger-springboot app, is handled inside &lt;code&gt;application.yml&lt;/code&gt;. This configuration section refers, mainly to the configuration of Swagger-UI .&lt;/p&gt;

&lt;p&gt;The Json returned at the RequestMapping "/swagger-resources/configuration/ui" contains, among other things, a 'filter' flag which, despite its value being boolean, defaults to true.&lt;/p&gt;

&lt;p&gt;I have taken the opportunity to remove that property, so accepting the default, given that there may be a large number of consolidated APIs to search through. The only property required, in order that the Swagger document, renders correctly is ‘supportedSubmitMethods’, and, even that, defaults to &lt;em&gt;all&lt;/em&gt; when it is set to an empty List. I have explicitly added all methods, for documentation purposes.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;


&lt;span class="c1"&gt;// /swagger-resources/configuration/ui&lt;/span&gt;

&lt;span class="n"&gt;uiConfigurationMap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"supportedSubmitMethods"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"get"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"put"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"post"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"delete"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"options"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"head"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"patch"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"trace"&lt;/span&gt;
&lt;span class="o"&gt;));&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I should highlight the aforementioned piece of configuration (or lack thereof), ‘filter’. It is the filtering ability provided here, that makes a strong case for this method of consolidation, over depending on the dropdown seen in other consolidation efforts.&lt;/p&gt;

&lt;p&gt;As well as the configuration above, there is an opportunity to change some of the static values, appearing on the finished document, this is achieved by, in effect, overriding the Json located at, or returned by, the path "/swagger-resources", this is also hard-coded. In both cases of the aforementioned hard-coding of configuration, the following software coding pattern is used: A ‘protected static’ String, for example &lt;em&gt;swaggerResource&lt;/em&gt;, was declared. Note, this is not declared final; this is because it will later be set to equal a &lt;em&gt;jsonised&lt;/em&gt; &lt;code&gt;Map&lt;/code&gt;, populated inside a static block, and later returned by a custom controller. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;


&lt;span class="c1"&gt;// /swagger-resources&lt;/span&gt;
 &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;swaggerResourceMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LinkedTreeMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class="n"&gt;swaggerResourceMap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Consolidated"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;swaggerResourceMap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/v2/api-docs"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;swaggerResourceMap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"swaggerVersion"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2.0"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;swaggerResource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"["&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Gson&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toJson&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;swaggerResourceMap&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"]"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The default document title is simply a semicolon-separated list of all the featured APIs. This list is first compiled and placed, as a list, on the consolidated &lt;code&gt;Map&lt;/code&gt;, with the key “info”, (info.title). Then, this list is used to create the title as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;INFO&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TITLE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SEMICOLON&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Iterable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;CharSequence&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;INFO&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TITLE&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Additionally, this title is configurable, by including the following in &lt;code&gt;application.yml&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;swagger-vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;docTitle &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Consolidated&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Doc,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;example'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Finished Product
&lt;/h2&gt;

&lt;p&gt;The following screenshot portrays the swagger UI consolidation of four connected APIs, with the user utilising the filter functionality to great effect.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7517fzq58fjo8pwtfdz7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7517fzq58fjo8pwtfdz7.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
Fig1.2 &lt;em&gt;A screenshot of the homepage, with filtering enabled&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Dependencies
&lt;/h2&gt;

&lt;p&gt;In order to get this to compile, java 12 will be required.&lt;/p&gt;

&lt;p&gt;Inside pom.xml, I have declared the following dependencies of note.&lt;/p&gt;

&lt;p&gt;Springfox&lt;br&gt;
This allows interplay between Spring and Swagger.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.springfox&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;springfox-swagger-ui&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.9.2&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Google’s GSON&lt;br&gt;
This is used to unmarshall the json strings, and later re-marshall the merged &lt;code&gt;Map&lt;/code&gt; back to a consolidated Json String.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.google.code.gson&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;gson&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.4&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Summary of Process
&lt;/h2&gt;

&lt;p&gt;Provide a mechanism for the user to configure the urls for the APIs to be consolidated.&lt;/p&gt;

&lt;p&gt;Take each of the target APIs’ Json; next we convert each Json string to some &lt;code&gt;Map&lt;/code&gt; implementation, then we merge those &lt;code&gt;Maps&lt;/code&gt;. Finally, we convert that merged &lt;code&gt;Map&lt;/code&gt; to Json and make that Json available for swaggerui to display.&lt;/p&gt;

&lt;p&gt;Meanwhile, Springfox would have also made other Json available to Swagger-Ui, based on annotations provided by the developer. We simply create controllers that manage the compilation of that meta-data.&lt;/p&gt;
&lt;h2&gt;
  
  
  Docker
&lt;/h2&gt;

&lt;p&gt;There is a docker Image available on Docker Hub, including a springboot app, and by creating a container from this image, you will see a working example of this app, which consolidates 4 APIs. &lt;/p&gt;

&lt;p&gt;In order to create a container from this image you must first install docker on your local machine. Then, simply run the following command:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; docker container run &lt;span class="nt"&gt;-p&lt;/span&gt; 80:8080 declantreanor/swaggerui:final


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The app will then be viewable at the url &lt;a href="http://localhost/swagger-ui.html" rel="noopener noreferrer"&gt;http://localhost/swagger-ui.html&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The username/password you’ll be prompted for is bob/bob.&lt;/p&gt;

&lt;h2&gt;
  
  
  Offline Mode
&lt;/h2&gt;

&lt;p&gt;There is an offline mode; Assuming at least one online attempt has been made, thereafter the &lt;code&gt;Json&lt;/code&gt; captured during that online attempt will be used for future attempts, unless, of course, that attempt takes place online, in which case, it will be business-as-usual.&lt;/p&gt;

&lt;p&gt;There are clear use cases for this feature, which was achieved with simple File I/O.&lt;/p&gt;

</description>
      <category>java</category>
      <category>docker</category>
      <category>documentation</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
