Skip to content

Commit

Permalink
Fix: Render OpenAPI Markdown with RedCarpet
Browse files Browse the repository at this point in the history
As noted in [0], the ability to render a CommonMark document through
OpenAPI specifications' `info.description` is breaking due to how the
Table of Contents code works.

We'd assumed that the `id` would always be set on headers - by the
`TechDocsHTMLRenderer` - but as we've been rendering the markdown
through `openapi3_parser`'s CommonMark implementation, these IDs are not
being populated.

Until the `openapi3_parser` gem supports making this configurable, which
is unlikely, due to the way that CommonMark doesn't have this as a
default extension, we can instead utilise our `TechDocsHTMLRenderer` to
render the headings with IDs, as expected.

To do this, we can create a new helper in `ApiReferenceRenderer` which
will render the Markdown document as our custom Markdown, which requires
we construct the `TechDocsHTMLRenderer` class, which needs the Middleman
`ConfigContext` class injected in.

As well as the original `info.description` that we'd planned to amend,
we can also replace any other CommonMark->HTML rendering at the same
time.

This means that we won't be using CommonMark, as per the OpenAPI spec,
and this is something we'll look to address in alphagov#282.

The setup for our tests are now a little more complex, as we're not able
to (easily) inject `RedCarpet`, so need to mock a bit more to make it
work.

[0]: alphagov/tdt-documentation#156
  • Loading branch information
jamietanna committed Nov 12, 2021
1 parent ee6b8aa commit 29362c9
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 9 deletions.
10 changes: 10 additions & 0 deletions lib/govuk_tech_docs/api_reference/api_reference_renderer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class Renderer
def initialize(app, document)
@app = app
@document = document
@redcarpet = build_redcarpet(app)

# Load template files
@template_api_full = get_renderer("api_reference_full.html.erb")
Expand Down Expand Up @@ -137,6 +138,11 @@ def schema_properties(schema_data)

private

def build_redcarpet(app)
renderer = GovukTechDocs::TechDocsHTMLRenderer.new(context: app.config_context)
Redcarpet::Markdown.new(renderer)
end

def get_renderer(file)
template_path = File.join(File.dirname(__FILE__), "templates/" + file)
template = File.open(template_path, "r").read
Expand All @@ -159,6 +165,10 @@ def get_schema_link(schema)
id = "schema-#{schema.name.parameterize}"
"<a href='\##{id}'>#{schema.name}</a>"
end

def render_markdown(text)
@redcarpet.render(text) if text
end
end
end
end
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<h1 id="<%= info.title.parameterize %>"><%= info.title %> v<%= info.version %></h1>
<%= info.description_html %>
<%= render_markdown(info.description) %>

<%# OpenAPI files default to having a single server of URL "/" %>
<% if servers.length > 1 || servers[0].url != "/" %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<p><em><%= operation.summary %></em></p>
<% end %>
<% if operation.description %>
<%= operation.description_html %>
<%= render_markdown(operation.description) %>
<% end %>

<%= parameters %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<td><%= parameter.in %></td>
<td><%= parameter.schema.type %></td>
<td><%= parameter.required? %></td>
<td><%= parameter.description_html %>
<td><%= render_markdown(parameter.description) %>
<% if parameter.schema.enum %>
<p>Available items:</p>
<ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<tr>
<td><%= key %></td>
<td>
<%= response.description_html %>
<%= render_markdown(response.description) %>
<% if response.content['application/json']
if response.content['application/json']["example"]
request_body = json_prettyprint(response.content['application/json']["example"])
Expand Down
4 changes: 2 additions & 2 deletions lib/govuk_tech_docs/api_reference/templates/schema.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<h3 id="<%= id = 'schema-' + title; id.parameterize %>"><%= title %></h3>
<%= schema.description_html %>
<%= render_markdown(schema.description) %>
<% if properties.any? %>
<table class='<%= id.parameterize %>'>
<thead>
Expand All @@ -11,7 +11,7 @@
<td><%= property_name %></td>
<td><%= property_attributes.type %></td>
<td><%= schema.requires?(property_name) %></td>
<td><%= property_attributes.description_html %></td>
<td><%= render_markdown(property_attributes.description) %></td>
<td>
<%=
linked_schema = property_attributes
Expand Down
25 changes: 22 additions & 3 deletions spec/api_reference/renderer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,34 @@
"info": {
"title": "title",
"version": "0.0.1",
"description": "# This is a header\n\nAnd a piece of _text_",
},
"paths": {},
}
@app = double("Middleman::Application")
@config_context = double("Middleman::ConfigContext")
allow(@app).to receive(:config_context).and_return @config_context
allow(@config_context).to receive(:app).and_return @app
allow(@app).to receive(:api) { |arg| arg }
end

it "renders the description" do
document = Openapi3Parser.load(@spec)

render = described_class.new(@app, document)
rendered = render.api_full(document.info, document.servers)

rendered = Capybara::Node::Simple.new(rendered)
expect(rendered).to have_css("h1", exact_text: "This is a header")
expect(rendered).to have_css("h1#this-is-a-header")
expect(rendered).to have_css("p", exact_text: "And a piece of text")
expect(rendered).to have_css("p>em", exact_text: "text")
end

it "renders no servers" do
document = Openapi3Parser.load(@spec)

render = described_class.new(nil, document)
render = described_class.new(@app, document)
rendered = render.api_full(document.info, document.servers)

rendered = Capybara::Node::Simple.new(rendered)
Expand All @@ -33,7 +52,7 @@
]
document = Openapi3Parser.load(@spec)

render = described_class.new(nil, document)
render = described_class.new(@app, document)
rendered = render.api_full(document.info, document.servers)

rendered = Capybara::Node::Simple.new(rendered)
Expand All @@ -49,7 +68,7 @@
]
document = Openapi3Parser.load(@spec)

render = described_class.new(nil, document)
render = described_class.new(@app, document)
rendered = render.api_full(document.info, document.servers)

rendered = Capybara::Node::Simple.new(rendered)
Expand Down

0 comments on commit 29362c9

Please sign in to comment.