Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API reference generated from Open API spec file #40

Merged
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
a7226bd
GTD-30 initial commit of api reference
mikebell Jul 3, 2018
07fc8c4
GTD-30 Add support for loading the config file and checking api_path …
mikebell Jul 3, 2018
8bfbb38
GTD-30 Add openapi3_parser to gemspec and update api reference parsin…
mikebell Jul 10, 2018
86fb375
GTD-30 initial commit of api reference
mikebell Jul 3, 2018
91f8974
GTD-30 Add support for loading the config file and checking api_path …
mikebell Jul 3, 2018
425ed9c
GTD-30 Add openapi3_parser to gemspec and update api reference parsin…
mikebell Jul 10, 2018
9751b43
Merge branch 'feature/GTD-30-api-reference-new' of https://github.com…
lewisnyman Jul 11, 2018
e54222b
GTD-30: Move render mark up to a template file
lewisnyman Jul 11, 2018
ccb9689
GTD-30 Add option to load api file from remote url
mikebell Jul 11, 2018
3b16475
GTD-30: Renamed template file. Loop through all available operations …
lewisnyman Jul 11, 2018
bb62ccc
GTD-30: Render operation parameters
lewisnyman Jul 11, 2018
0544303
GTD-30: Print operation responses.
lewisnyman Jul 12, 2018
0a4f376
GTD-30: Add proper id attributes to headings
lewisnyman Jul 12, 2018
8b708c4
GTD-30 Add support for printing out full api spec
mikebell Jul 16, 2018
ca4947a
GTD-30: Print all api paths in a document
lewisnyman Jul 17, 2018
98c22f0
GTD-30: Render api info and server
lewisnyman Jul 17, 2018
2602617
GTD-30 Correct server to servers
mikebell Jul 18, 2018
dd4a689
GTD-30: Fix server rendering
lewisnyman Jul 18, 2018
ce68d0c
GTD-30 Add components function to return components schema
mikebell Jul 18, 2018
db50d2b
GTD-30: Added pry with an example use to help inspecting Openapi3Pars…
lewisnyman Jul 18, 2018
c1b32b5
GTD-30: Initial rendering of schemas
lewisnyman Jul 18, 2018
499c82e
GTD-30: Print schema properties
lewisnyman Jul 19, 2018
786d7df
GTD-30: Remove pry
lewisnyman Jul 19, 2018
4d296cb
GTD-30: Link schema references to schemas on the page
lewisnyman Jul 19, 2018
2e854fe
GTD-30: Move operations to function, make it less fragile
lewisnyman Jul 19, 2018
66a427a
GTD-30: Move api reference files into a sub-folder
lewisnyman Jul 19, 2018
f031dd6
GTD-60 First pass at rubocop linting
mikebell Jul 23, 2018
e4d32f1
GTD-30: Support markdown in object descriptions.
lewisnyman Jul 23, 2018
5f8f7e7
Merged: GTD-60 First pass at rubocop linting
lewisnyman Jul 23, 2018
912c261
GTD-60 Always track latest version of openapi3_parser
mikebell Jul 30, 2018
fbcc319
GTD-30 Render description as markdown
mikebell Jul 30, 2018
318fad3
GTD-30 Fix broken html h4 header
mikebell Jul 30, 2018
f506593
GTD-30 Support markdown in schema and property descriptions
mikebell Jul 30, 2018
0baf0cb
GTD-30 Add conditional if schema doesnt exist
mikebell Jul 30, 2018
ac02d34
Revert "GTD-30 Add conditional if schema doesnt exist"
mikebell Jul 30, 2018
4ce8f0c
GTD-30 Add check to see if text exists before running tilt on it
mikebell Jul 30, 2018
2cacadc
GTD-30: Add integration tests for full api and single path output.
lewisnyman Jul 30, 2018
b152eae
Merge alphagov/master
lewisnyman Jul 30, 2018
6d9f458
GTD-30: Fix broken integration spec
lewisnyman Jul 30, 2018
b540f87
GTD-60 Fix issues brought up by rubocop
mikebell Jul 30, 2018
400e3eb
GTD-30: Fixed remaining rubocop offenses
lewisnyman Jul 30, 2018
51e9e97
GTD-60: Add schema parameter check
lewisnyman Jul 30, 2018
0b674d1
GTD-30: Add configuration documentation
lewisnyman Jul 30, 2018
f94a470
GTD-30 Refactor api method and add methods for path and schemas
mikebell Aug 1, 2018
31621db
GTD-30 Removed properties header in schema template
mikebell Aug 1, 2018
b235f2a
GTD-30 Refactor how schema objects are built and added to render
mikebell Aug 1, 2018
ca0ae9b
Revert "GTD-30 Refactor how schema objects are built and added to ren…
mikebell Aug 1, 2018
7be0c58
GTD-30 Fix schema rendering for full_api
mikebell Aug 1, 2018
7492727
GTD-30 Rename map array to keywords
mikebell Aug 1, 2018
d77d2a5
GTD-30 Add parenthesis around uri check
mikebell Aug 1, 2018
51115f4
GTD-30 Raise exceptions where required
mikebell Aug 1, 2018
59d3473
Fix linting errors
lewisnyman Aug 2, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,11 @@ default: `true`
```yaml
show_govuk_logo: true
```

## `api_path`

Define a path to an Open API V3 spec file. This can be a relative file path or a URI to a raw file.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should say what gets generated when you provide this.

From the example it looks like you need to create a source file like this as well as specifying the api path to get any output?

---
title: Example API Petstore
---
 api>


```yaml
api_path: ./source/pets.yml
```
2 changes: 2 additions & 0 deletions example/config/tech-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,5 @@ github_repo: alphagov/example-repo

redirects:
/something/old.html: /index.html

api_path: source/pets.yml
7 changes: 7 additions & 0 deletions example/source/api-path.html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: API /Pets
---

# API /Pets

api> /pets
5 changes: 5 additions & 0 deletions example/source/api-reference.html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
title: Example API Petstore
---

api>
106 changes: 106 additions & 0 deletions example/source/pets.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: Swagger Petstore
description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification
license:
name: MIT
servers:
- url: http://petstore.swagger.io/v1
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of pets
headers:
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a pet
operationId: createPets
tags:
- pets
responses:
'201':
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
2 changes: 2 additions & 0 deletions govuk_tech_docs.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Gem::Specification.new do |spec|
spec.add_dependency "middleman-search"
spec.add_dependency "nokogiri"
spec.add_dependency "redcarpet", "~> 3.3.2"
spec.add_dependency "openapi3_parser"
spec.add_dependency "pry"


spec.add_development_dependency "bundler", "~> 1.15"
Expand Down
7 changes: 6 additions & 1 deletion lib/govuk_tech_docs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
require 'govuk_tech_docs/tech_docs_html_renderer'
require 'govuk_tech_docs/unique_identifier_extension'
require 'govuk_tech_docs/unique_identifier_generator'
require 'govuk_tech_docs/api_reference/api_reference'

module GovukTechDocs
# Configure the tech docs template
Expand All @@ -37,7 +38,9 @@ def self.configure(context, options = {})
context.set :markdown_engine, :redcarpet
context.set :markdown,
renderer: TechDocsHTMLRenderer.new(
with_toc_data: true
with_toc_data: true,
api: true,
context: context
),
fenced_code_blocks: true,
tables: true,
Expand All @@ -56,6 +59,8 @@ def self.configure(context, options = {})
context.config[:tech_docs] = YAML.load_file('config/tech-docs.yml').with_indifferent_access
context.activate :unique_identifier

context.activate :api_reference

context.helpers do
include GovukTechDocs::TableOfContents::Helpers
include GovukTechDocs::ContributionBanner
Expand Down
173 changes: 173 additions & 0 deletions lib/govuk_tech_docs/api_reference/api_reference.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
require 'erb'
require 'openapi3_parser'
require 'uri'
require 'pry'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this pry can be removed


module GovukTechDocs
class ApiReference < Middleman::Extension
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this extension would be usable by other middleman projects not using the tech-docs-gem?

If so it would be great if we could package this up separately at some point, to contribute back to the wider ecosystem of openapi tools.

expose_to_application api: :api

def initialize(app, options_hash = {}, &block)
super

@app = app
@config = @app.config[:tech_docs]

# If no api path then just return.
if @config['api_path'].to_s.empty?
# @TODO Throw a middleman error?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this raise an error that tells you what you need to add?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in latest commit.

return
end

# Is the api_path a url or path?
if uri?@config['api_path']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor style thing but it looks weird that there's no space between the method call and its argument here, can you change it to use the parentheses style?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in new commit

@api_parser = true
@document = Openapi3Parser.load_url(@config['api_path'])
elsif File.exist?(@config['api_path'])
# Load api file and set existence flag.
@api_parser = true
@document = Openapi3Parser.load_file(@config['api_path'])
else
# @TODO Throw a middleman error?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another TODO here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in latest commit

@api_parser = false
end

# Load template files
@render_api_full = get_renderer('api_reference_full.html.erb')
@render_path = get_renderer('path.html.erb')
@render_schema = get_renderer('schema.html.erb')
end

def uri?(string)
uri = URI.parse(string)
%w(http https).include?(uri.scheme)
rescue URI::BadURIError
false
rescue URI::InvalidURIError
false
end

def api(text)
if @api_parser == true

map = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you give this a more descriptive name? This makes the following line a bit harder to parse (map.map)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to keywords in a new commit.

'api&gt;' => 'default',
'api_schema&gt;' => 'schema'
}

regexp = map.map { |k, _| Regexp.escape(k) }.join('|')

md = text.match(/^<p>(#{regexp})/)
if md
key = md.captures[0]
type = map[key]

text.gsub!(/#{ Regexp.escape(key) }\s+?/, '')

# Strip paragraph tags from text
text = text.gsub(/<\/?[^>]*>/, '')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would also strip other tags from the text, if there are any. Is it valid for other stuff to appear in the paragraph besides the api> line?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not at the moment, it only works if that's the only thing on the line. Is there a use case where you'd want to include this in a paragraph?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so - requiring it to be in its own paragraph makes sense I think.

text = text.strip

if type == 'default'
api_path_render(text)
else
api_schema_render(text)
end

else
return text
end
else
text
end
end

def api_path_render(text)
if text == 'api&gt;'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this forgive trailing whitespace after the api>?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it does from my testing.

return api_full
else
# Call api parser on text
path = @document.paths[text]
output = @render_path.result(binding)
return output
end
end

def api_schema_render(text)
schemas = ''
schemas_data = @document.components.schemas
schemas_data.each do |schema_data|
if schema_data[0] == text
print schema_data[0]
print schema_data[1]
end
end
return text
end

def api_full
info = api_info
server = api_server

paths = ''
paths_data = @document.paths
paths_data.each do |path_data|
# For some reason paths.each returns an array of arrays [title, object]
# instead of an array of objects
text = path_data[0]
path = path_data[1]
paths += @render_path.result(binding)
end
schemas = ''
schemas_data = @document.components.schemas
schemas_data.each do |schema_data|
title = schema_data[0]
schema = schema_data[1]
schemas += @render_schema.result(binding)
end
@render_api_full.result(binding)
end

def render_markdown(text)
if text
Tilt['markdown'].new(context: @app) { text }.render
end
end

private

def get_renderer(file)
template_path = File.join(File.dirname(__FILE__), 'templates/' + file)
template = File.open(template_path, 'r').read
ERB.new(template)
end

def get_operations(path)
operations = {}
operations['get'] = path.get if defined? path.get
operations['put'] = path.put if defined? path.put
operations['post'] = path.post if defined? path.post
operations['delete'] = path.delete if defined? path.delete
operations['patch'] = path.patch if defined? path.patch
operations
end

def api_info
@document.info
end

def api_server
@document.servers[0]
end

def get_schema_name(text)
unless text.is_a?(String)
return nil
end
# Schema dictates that it's always components['schemas']
text.gsub(/#\/components\/schemas\//, '')
end
end
end

::Middleman::Extensions.register(:api_reference, GovukTechDocs::ApiReference)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<h1 id="<%= info.title.parameterize %>"><%= info.title %> v<%= info.version %></h1>
<%= render_markdown(info.description) %>
<% if server %>
<h2 id="base-url">Base URL</h2>
<p><strong><%= server.url %></strong></p>
<% end %>
<%= paths %>
<h2 id="schemas">Schemas</h2>
<%= schemas %>
Loading