Creating a developer portal with no budget (part 3/3)

illustrations illustrations illustrations illustrations illustrations illustrations illustrations
post-thumb

Published on 3 February 2022 by Andrew Owen (11 minutes)

In this three part series, I’ll outline how to create a fully featured dev portal for your Swagger or OpenAPI 3.0 content without spending a dime. You can read part one here and part two here.

Part III: Writing

Most of this relates specifically to writing in-line docs directly in C# code using Swashbuckle. However, if you’re using a different approach you can skip ahead to the section on Markdown.

Swashbuckle

Autogenerated Swagger / OpenAPI 3.0

Swashbuckle is a framework for ASP.NET Core that converts code comments into Swagger or OpenAPI 3.0 docs. If your developers write in C# and they’re not already using a top-down API methodology, it’s well worth a look.

Document API methods with multiple domain models

Some APIs use domain models to perform more than one task using a single API method.

Swagger shows the various domain models as different options in the Request Body Schema. However, it always shows the payload for the first domain model, regardless of the domain model selected.

For these API methods, the schema should be documented in a separate HTML page with a relative link, served by the static site generator.

Documentation for these API methods is declared in a private class as follows:

    postPath.Post.Summary = "Short name";
    postPath.Post.Description = "Long description";

The string must be contained on a single line. If you need to insert a carriage return you must use the HTML <br/> tag.

The long description should contain the following text:

The request body schema shown applies only to the first listed command.

This should be followed by a relative link to a markdown page describing the various commands.

Create API docs in VS Code

With Swashbuckle, API documentation is created as comments in the C♯ source code (.cs files). These comments are automatically converted to Swagger / OpenAPI 3.0 docs when the application is built. Most content can be written in markdown.

Tag API docs in the code

To make it clear in the code that the comments will be public facing, you can use XML comment format tags around the other comments:

    /// <!--apiddocs-->
    /// ...
    /// <!--/apiddocs-->
API group descriptions

To enable a summary for a group, the Startups.cs file must include the following:

    if (File.Exists(xmlPath))
    {
        c.IncludeXmlComments(xmlPath, true);
    }
API methods

Typically, methods are associated with a particular API. However, some methods are inherited and must be edited separately. Documentation can be added using these tags:

  • summary (plain text): A short description of the method. If no summary is provided, the API name is used.
  • remarks: A description of the method.
  • param: A short description of the input parameters.
  • response: A short description of the response code.
Example:
    /// <!--apidocs-->
    /// <summary>
    /// Find product by SKU.
    /// </summary>
    /// <remarks>
    /// Find a product where the \`sku\` is known.
    /// </remarks>
    /// <param name="sku">Stock keeping unit.</param>
    /// <response code="400">Bad request.</response>
    /// <!--/apidocs-->
    [SwaggerOperation(Taqs = new[] { "Products" })]
    [HttpGet(Name = GetProductBySkuRoute)]
    ...
Response parameters

Typically, responses for a given API are grouped in a single file. Documentation can be added using summary and example tags. You can find parameters in a project by searching files for [JsonProperty. The example tags are always converted to a single string so they should only be used with the following types:

  • enum (example: DateTimeOffset)
  • GUID (globally unique identifier)
  • string

Where you need to provide an example for other types, add a carriage return in the last line of the summary, followed by Example: . Don’t use example tags for arrays. If you use an array example in the summary, you must escape the first square brace (\[, ]).

Example:
    /// <!--apidocs-->
    /// <summary>
    /// Stock keeping unit.
    /// </summary>
    /// <example>
    /// 6502_RICE_1KG
    /// </example>
    /// <!--/apidocs-->
    [JsonProperty{required = Required.Always}]
    public string Sku { get; set; }
Required parameters

When parameters are required, you can mark them with [Required], but the .cs file will need a using System.ComponentModel.DataAnnotations; declaration at the beginning of the file.:

If there is already a [ JsonProperty(Required = Required.DisallowNull)] then do not use [Required].

Example:
    ...
    /// <!--/apidocs-->
    [Required]
    public bool AddProductToBasket { get; set; }
Commenting out comments

If you need to prevent the comments being converted to XML so that you can see what the method name is in the ReDoc output, you can use XML comment syntax:

    /// <!-- <summary>
    /// Stock keeping unit.
    /// </summary> -->

In XML, certain characters must be escaped or the XML will fail to build from the comments (without warnings). For example, ampersands (&amp;) and angle brackets, (&lt;, &gt;). If you need to use an ampersand with code font style, you must use HTML <code> tags. If you use the markdown backtick (```) the escaped ampersand won’t be converted to a single character.

Markdown

All tagged comments can contain plain text. Some can also contain markdown. Any that can contain markdown can also contain HTML. However, the way markdown and HTML are rendered will vary depending on the tag.

Style Markdown
heading level 1 # Heading 1
heading level 2 ## Heading 2
heading level 3 ### Heading 3
bold __bold__
italic _italic_
code font `code`
ordered list 1. item 1 1. item 2 1. item 2.11. item 3
itemized list * item 1* item 2 * item 2.1* item 3</pre<>
web link [web link](https://www.example.com)
relative link [relative link](../flowcharts/flowchart.md)
email link [email link](mailto:api@example.com?subject=API%20doc%20request&body=I%20am20using20the20APINAME20API20and20require20further20documentation.)
table with header | Firstname | Lastname | Age || ——— | ——– | — || Jill | Smith | 50 || Eve | Jackson | 94 |
line break: created by ending a line with a backslash. These must be tagged with an XML comment to alert developers that the backslash is intentional. /// Long description.\ /// [Request docs](mailto:api@example.com)
program listing In tags, use ``` above and below each code block. In tags, do not use ```. Ensure all other text runs on from the start and each line ends with \ /// Then add the code, starting on the next comment line: { “id”: “string” }

Mermaid

Create charts with Mermaid markdown

Mermaid is a markdown extension that simplifies the creation of charts. You can add a Mermaid extension to themes used by the Hugo static site generator. You can preview the markdown in VScode using the Markdown Preview Enhanced extension.

Before saving the file you must change ```mermaid to {{<mermaid>}} and ``` to {{</mermaid>}}.

Examples:
    ```mermaid
    graph TD;
    A-->B;
    A-->C;
    B-->D;
    C-->D;
    ```
    ```mermaid
    graph LR;
    A-->B;
    A-->C;
    B-->D;
    C-->D;
    ```
    ```mermaid
    sequenceDiagram
    participant User
    participant Mobile App
    participant STS
    participant API
    User->>Mobile App: 1. Click login link.
    loop challenge
    Mobile App->Mobile App: 2. Generate code verifier and code challenge.
    end
    Mobile App->>STS: 3. Authorization code request and code challenge to authorize.
    STS->>User: 4. Redirect to login/authorization prompt.
    User->>STS: 5. Authenticate and consent
    STS->>Mobile App: 6. Authorize code.
    Mobile App->>STS: 7. Authorization code and code verified to OAuth token.
    
    loop validate
    STS->STS: 8. Validate code verifier and challenge.
    end
    
    STS->>User: 9. ID token and access token.
    User->>API: 10. Request user data with access token.
    API->>User: 11. Response.
    ```
    ```mermaid
    gantt
    dateFormat YYYY-MM-DD
    title Adding GANTT diagram functionality to mermaid
    section A section
    Completed task:done, des1, 2014-01-06,2014-01-08
    Active task:active, des2, 2014-01-09, 3d
    Future task: des3, after des2, 5d
    Future task2: des4, after des3, 5d
    section Critical tasks
    Completed task in the critical line:crit, done, 2014-01-06,24h
    Implement parser and jison:crit, done, after des1, 2d
    Create tests for parser:crit, active, 3d
    Future task in critical line:crit, 5d
    Create tests for renderer:2d
    Add to mermaid:1d
    ```

Troubleshooting

Create a PDF from OpenAPI JSON

On occasion you may be asked to produce a version of your dev portal as a PDF file. Distilling an interactive website into a PDF is impossible. Most browsers won’t even let you print the site in its entirety. However, you can use Swagger2PDF to generate a simple PDF from an OpenAPI JSON file:

swagger2pdf -s swagger.json
Ensure new features and changes are documented

Doc teams typically cover the work of multiple development teams, creating developer and user docs. It’s not always possible to attend every scrum meeting and sprint review.

To ensure developer and user docs are delivered as close as possible to the sprint in which features are delivered, it is essential that the team receives timely notification.

There are three ways to do this in Jira:

  • Create a Documentation sub task on a ticket.
  • Create a Documentation task and associate it with a ticket.
  • Add the Documentation label to a ticket.

You should configure a doc team kanban board to display tickets that meet any of these criteria. You can then watch a ticket to track its progress. Let your developers know how to create doc tickets:

  • You can request new documentation for existing features by creating a Documentation task.
  • The doc team will prioritize documentation requests to best meet customer needs.
  • Requests for documentation that are made other than using Jira will be given the lowest priority.
Fix a broken API
Things to try:
  • Does the start of the .cs file have the following:

    using Swashbuckle.AspNetCore.SwaggerGen;
    
  • Are there incorrectly declared types?

    Type = "integer" / Type = "boolean"
    
  • If the comments are absent from the JSON is the DocumentationFile property set in the .csproj file (should be set for debug and release). Example:

    <DocumentationFile>bin\Debug\netcoreapp2.2\Your.ApiGateway.xml</DocumentationFile>
    
Convert OpenAPI YAML to JSON

ReDoc requires OpenAPI 3.0 / Swagger 2.0 source in JSON format to create a static HTML page. Typically, auto-generated content, such as that produced by Swashbuckle, is already in JSON format. However, hand edited API schema are typically created in YAML.

  1. Navigate to https://editor.swagger.io/. Alternatively, download the Swagger Editor to use keep the content inside the corporate network.
  2. Select File > Import File.
  3. Fix any critical schema errors.
  4. Select File > Convert and save as JSON.
Convert Swagger 2.0 to OpenAPI 3.0 to resolve schema errors

ReDoc requires schema in OpenAPI 3.0 format. If you provide a Swagger 2.0 JSON file, it will attempt to do the conversion itself, but schema errors may cause the conversion to fail. Ideally you should fix the schema errors. However, if you require a quick approximation of how the API will appear you can try using the Mermade online converter.

ReDoc produces a static HTML page. The easiest way to add it to a Hugo site while keeping the navigation in the header is to use an iFrame. A better approach would be to ingest the page and dynamically recreate the whole thing. But that would require work by an experienced Hugo developer.

The quick and dirty approach is:

  1. Add <base target="_parent"> in the <head> section of the ReDoc HTML file.
  2. Put the HTML pages in Hugo’s static folder.
  3. Create a markdown page in Hugo’s content folder:
    <pre>
    ---
    ---
    <iframe src="../../../markdownfile/" frameborder="0" allowfullscreen style="position:absolute;top:1;left:0;width:100%;height:100%;"></iframe></pre>

In the API docs, clicking a method link in the left pane in ReDoc should take the user to the appropriate method. However, when developers reuse Operation IDs, the link will instead take them to the first instance of that Operation ID. Developers must either use Operation IDs that are unique across the entire API gateway, or use no Operation IDs at all.

Remove .DS_Store files from a Git repository
  1. In the shell, navigate to the root of the repository.
  2. Enter find . -name .DS_Store -print0 | xargs -0 git rm -f --ignore-unmatch .
  3. Commit changes.
Install Java 8

Oracle changed the license for JDK 8, effective 16 April 2019. You can still download it, but if in doubt, you can use penJDK 8 instead (https://www.azul.com/downloads/zulu/).

Run Windows apps on Intel Macs

Because API doc tools make heavy reliance on UNIX-land command line tools, it may be preferable to use a Mac. However, traditional technical writing tools may be Windows-only, for example MadCap Flare. The easiest way to run Windows programs on an Intel Mac is with Parallels. However, Intel Macs are going away. I’ll cover the options for running Windows programs on an M1 Mac in a future article.

Run a local Rabbit MQ

Some API gateways have RabbitMQ as a build dependency. Because the remote server isn’t always available, you may need to run a local instance of RabbitMQ. You can use Docker to do this from the command prompt:

    docker run -d --name rabbitmq -p 15672:15672 -p 5672:5672 bitnami/
    rabbitmq:latest
  1. Navigate to http://localhost:15672/ and create a user called rabbituser with the password rabbituser as an administrator.
  2. Click the rabbituser user and in the Virtual Host section, then click Set permission.
  3. In the source code, locate the appsettings.Development.json file and change all instances of guest to rabbituser .

To restart the service:

    docker start rabbitmq

To remove an old Rabbit MQ:

    docker system prune -a

As a final thought, you really need a writing style guide. If you don’t have one you should create one. I’ll cover that topic in a future article.