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

Conversation

lewisnyman
Copy link
Contributor

This branch allows you to define a path to an Open API file in tech-docs.yml: api_path: source/pets.yml. This can be a relative file path or a URI to a raw file elsewhere.

To output the generated content in the page of your choice, write api> in any markdown parsed file (.md, .html.md, .html.md.erb). You can also specify to just print a specific path: api> /pets

See the example project on how to set this up.

mikebell and others added 30 commits July 3, 2018 14:35
…/ConvivioTeam/tech-docs-gem into feature/GTD-30-api-reference-new

* 'feature/GTD-30-api-reference-new' of https://github.com/ConvivioTeam/tech-docs-gem:
  GTD-30 Add openapi3_parser to gemspec and update api reference parsing code
  GTD-30 Add support for loading the config file and checking api_path is set
  GTD-30 initial commit of api reference
Schema reference is WIP
Also convert the class into a Middleman extension. This means it has access to the Middleman app instance.
We can use this to reference in the custom markdown engine and also get the config without having to pass the config yml file.
Performance bonus: The class is only initialised on start up, not on every paragraph
@lewisnyman
Copy link
Contributor Author

I've got an experimental branch attempting to add in some unit tests but it requires a big restructure as it seems to be very difficult to unit test a Middleman extension. I'm not even sure which functions are worth unit testing that isn't tested in the integration test as this extension doesn't have complex logic.

@MatMoore
Copy link
Contributor

I haven't looked at the code yet, but here's some initial feedback from looking at the pet store example.

I'll take a deeper look later today or tomorrow and check it against some different specs. With the previous solution I had a lot of trouble documenting responses with nested objects and arrays, so I'm keen to try out that use case.

Things that don't work well

On the "API /Pets" page where you embed the endpoint docs, the page doesn't include the schema section, which means that the schema links in the responses table don't work (the "Swagger Petstore v1.0.0" pages render these sections so the links work there).

I think we also need a page in the manual that explains the api > /pets syntax and when you might want to use it.

In the schema section, the headings are a bit confusing. There is an empty section rendered for "Pets" and the Error and Pet schemas have a subheading "properties" which could be removed.

Customisability

It seems like at the moment you have to choose whether you want everything autogenerated from the spec, or you have to create a markdown file for every endpoint, which is fine, but the ideal for me is probably somewhere in the middle.

For example, with the registers tech docs we moved "Rate limits" and "HTTP status codes" into the API reference section, and I'm not sure we'd still be able to do this if we were generating all the endpoint documentation from the spec.

When I tried out dapperdox a while back it was easier to customise, because the page is divided into multiple components and you can choose which bits you want to override.

Personally I would prefer if there was a separate page for every resource by default, rather than having it all on one page, because it feels quite information-dense. For a real API, the property tables can get very long, and I find it a bit annoying when if I scroll a bit too far I see information for some unrelated endpoint. This could be addressed in other ways though - Stripe do the single page thing very well, but they also have a lot of vertical space between stuff, which makes it easier to read.

@lewisnyman
Copy link
Contributor Author

@MatMoore Thanks for the feedback. There are a few bugs you've pointed out that we will fix today.

The customisability stuff, I think I understand what you're saying. Initially I was leaning towards single page as the primary use case, I think we got some initial feedback preferring having all the API information on one page for quicker searching.

You're right we could do a lot more make the content render from the spec more flexible. Are you able to give some examples of the types of parameters you would pass into api> to achieve what you're thinking?

I think if we wanted to move towards a more customisable system similar to Dapperdox then we could figure out a way to dynamically search for templates so you could override templates per-node.

@MatMoore
Copy link
Contributor

MatMoore commented Aug 1, 2018

@lewisnyman I don't know how viable this is, but I was thinking that if I was using api>/pets, it would be great if you could just dereference different parts of the spec like

  • api>/pets>get>parameters
  • api>/pets>get>responses>200
  • api>schemas>Pet

and be able to render them inline. But I would definitely consider this a 'nice to have' as I don't know whether I'd actually use the api>/pets method at all.

edit: you can ignore my comment above about having to choose between autogenerating everything and creating a markdown file for everything.

I was assuming that by default it would create the API reference page automatically, but after looking through the code I realised you still provide the source file with api> in it. So my concern about not being able to add other things to that section of the navigation tree isn't relevant.


## `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>

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

require 'pry'

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.


# 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.

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_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

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.

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.

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.

Copy link
Contributor

@MatMoore MatMoore left a comment

Choose a reason for hiding this comment

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

Other than the comment on the documentation, I don't think there is anything blocking this being merged once the tests pass.

I didn't get a chance to try out any other specs, but the pet store example looks fine, and we'll get more feedback once people start trying this with real APIs.

@jonathanglassman
Copy link
Contributor

@MatMoore travis check failed :(

@jonathanglassman jonathanglassman merged commit a9518f9 into alphagov:master Aug 6, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants