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

Update openapi_parser #125

Merged
merged 9 commits into from
Mar 15, 2021
Merged

Update openapi_parser #125

merged 9 commits into from
Mar 15, 2021

Conversation

kevindew
Copy link
Member

@kevindew kevindew commented Oct 14, 2019

Following up from #121 this updates openapi_parser to allow the latest version of gem. Since the gem is below version 1 it seemed safe to maintain the pin.

I've also done some cleaning up of some of the Ruby code in GovukTechDocs::ApiReference::Renderer to make it more shorter and more consistent with modern Ruby. There's a few dubious things in there that I left as I didn't want to risk creating fresh bugs from "fixing" stuff.

I've also rebased #113 onto this as that fix would conflict with the changes here.

Edit:

Since opening this @tijmenb closed #113 so the link to that is no longer accurate. I didn't want to re-instate a bug by undoing that so wanted to keep that change here and had already amended his change to use a new method in openapi_parser to check required status.

To document the issue, an OpenAPI document schema object has a required property which must be an array of strings which is used to indicate what properties of that object are required.

Previously the schema template just checked whether that array was set to determine if a parameter was required:

<td><%= property[1].required.present? %></td>

This is incorrect as this only tells you whether that parameter requires properties of its own.

Instead we use the #requires? method on the schema to determine if this property is required.

Copy link
Contributor

@ChrisBAshton ChrisBAshton left a comment

Choose a reason for hiding this comment

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

This looks like a fabulous improvement 🎉

if !schema_name.nil?
schemas.push schema_name
if property.name
schemas.push property.name
Copy link
Contributor

Choose a reason for hiding this comment

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

😍

# instead of an array of objects
text = path_data[0]
paths += path(text)
paths = @document.paths.keys.inject('') do |memo, text|
Copy link
Contributor

Choose a reason for hiding this comment

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

Coming from a non-Ruby background, inject threw me (I didn't realise it's just an alias for reduce).

Have tried searching online for what the Ruby convention is but it seems that isn't prescribed anywhere. But found a few articles in favour of using reduce, from notable people like Martin Fowler, who suggests it simply reads better.

If I suggested you change it to reduce, would that violate a ruby convention?

Copy link
Member Author

Choose a reason for hiding this comment

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

#inject pre-dates #reduce in Ruby so that seems the more idiomatic way to do it for me. I wouldn't suggest it needs changing here as we've never applied any preference in GDS code for one way or the other.

schema_properties(schema.items)
else
{}
end
Copy link
Contributor

Choose a reason for hiding this comment

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

This block feels like it could be refactored - something like:

memo[name] = schema_properties(schema.items || schema)
memo[name] = (schema.example || schema.type) unless memo[name]

But I might be missing a nuance there - and appreciate that you're concerned about introducing new bugs.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks - I've not been very happy with my changes there. I don't dare quite go to the level you suggest here as there are a few more nuances. I've done a bit of refactoring to try make it all a bit easier to read though.

'post' => path.post,
'delete' => path.delete,
'patch' => path.patch,
}.compact
Copy link
Contributor

Choose a reason for hiding this comment

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

🎉

CHANGELOG.md Outdated
## 2.0.5
## Unreleased

Fix bug in API docs that shows required attributes as optional
(#113)
Copy link
Contributor

Choose a reason for hiding this comment

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

Code looks good in this commit - but can you fix the URL in the commit message so that it's not split onto multiple lines?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure I've fixed that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Think we ought to remove this line from this commit now that it's been closed without merging. (Should mean the last commit isn't needed either).

CHANGELOG.md Outdated
## 2.0.5
## Unreleased

Fix bug in API docs that shows required attributes as optional
(#113)
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry @kevindew, closed #113 because I had misunderstood the relationship between JSON Schema and OpenAPI.

Copy link
Member Author

Choose a reason for hiding this comment

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

@tijmenb oh interesting - what was your concern there? I thought your logic was sound and the behaviour in current tech docs release is wrong.

Great to hear you're planning to push up a bunch of improvements for the API docs 👍 let me know if I can help at all

@kevindew kevindew force-pushed the use-openapi-parser-0.7 branch 3 times, most recently from 81f3dc3 to bdc50e0 Compare November 14, 2019 09:49
@kevindew
Copy link
Member Author

@ChrisBAshton Thanks for the review and apologies in taking so long to get back to this. I've fixed up various bits noted.

Copy link
Contributor

@ChrisBAshton ChrisBAshton left a comment

Choose a reason for hiding this comment

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

The angry seal reminded me about this PR - have given it a re-review. Looks good to go apart from outdated references to Tijmen's (closed) PR 👍

@@ -11,7 +11,7 @@
<td><%= property[0] %></td>
<td><%= property[1].type %></td>
<td><%= property[1].required.present? %></td>
<td><%= markdown(property[1].description) %></td>
<td><%= property[1].description_html %></td>
Copy link
Contributor

Choose a reason for hiding this comment

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

While you're here, could you apply the same changes to this file as you did in the "Rename some variables for clarity" commit?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think it's necessary to do a change here as it's done in Tijmen's commit which is next.

CHANGELOG.md Outdated
## 2.0.5
## Unreleased

Fix bug in API docs that shows required attributes as optional
(#113)
Copy link
Contributor

Choose a reason for hiding this comment

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

Think we ought to remove this line from this commit now that it's been closed without merging. (Should mean the last commit isn't needed either).

By using the name property on a Schema these look ups via node_context
can be removed.
The code in this file didn't appear to be very consistent with modern
Ruby patterns so I've given it something of a spruce up.

I was super nervous about the risk of introducing bugs or changing
relied upon quirks so I set myself a couple of rules: try to keep the API
identical and try keep the behaviour identical.

There are a bunch of dubious things going on in this code that does make
me a bit concerned for it's long term usage. I hope in doing this it
makes this code a little more inviting for the next person to come a
long and try make it better.
There are helper methods available to do this in the gem so this
renderer doesn't need it's own markdown conversion system.
@kevindew
Copy link
Member Author

@ChrisBAshton about 1 million years later, but this is good for a re-review if you don't mind? 🙏

Copy link
Contributor

@ChrisBAshton ChrisBAshton left a comment

Choose a reason for hiding this comment

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

LGTM 👍 some non-blocking comments below.

@@ -6,18 +6,18 @@
<tr><th>Name</th><th>Type</th><th>Required</th><th>Description</th><th>Schema</th></tr>
</thead>
<tbody>
<% properties.each do |property| %>
<% properties.each do |property_name, property_attributes| %>
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: the commit message confusingly refers to a attribute_name, I assume it should refer to property_name instead.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure - I've changed it (felt a bit weird editing someone else's commit)

@@ -55,5 +55,7 @@ def then_there_is_correct_api_schema_content
expect(page).to have_css("h3#schema-pet", text: "Pet")
# Schema parameters
expect(page).to have_css("table", text: /\b(tag )\b/)
# Check that the "required" column is true for the `id` attribute
expect(page).to have_css("table.schema-pet td:nth(3)", text: "true")
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be great if this test were a bit more self-documenting - I wonder if we can dynamically infer the correct column number? I appreciate that introduces some complexity to the test, so may not be an improvement.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't really want to edit someone else's code there, nor particularly going into the world of testing for the API stuff. It seems a big can of worms as there is a lack of testing for it all.

@@ -1,7 +1,8 @@
<h1 id="<%= info.title.parameterize %>"><%= info.title %> v<%= info.version %></h1>
<%= info.description_html %>

<% unless servers.empty? %>
<%# OpenAPI files default to having a single server of URL "/" %>
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<%# OpenAPI files default to having a single server of URL "/" %>
<% # OpenAPI files default to having a single server of URL "/" %>

Copy link
Member Author

Choose a reason for hiding this comment

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

<%# is actually erb syntax for a comment and means something different to <% #: https://docs.ruby-lang.org/en/2.3.0/ERB.html#class-ERB-label-Recognized+Tags

Copy link
Contributor

Choose a reason for hiding this comment

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

Ooh, TIL! 👀

tijmenb and others added 5 commits March 15, 2021 17:27
I'm trying to fix a bug, but I keep getting confused by the variables
in this each loop. This commit refactors it so that we can refer to
`property_name` and `property_attributes` (which is an object that we
get from OpenApi3Parser).
This commit fixes a bug OpenAPI reference renderer that affects which
attributes of a schema
(https://swagger.io/docs/specification/data-models/) are considered to
be required.

For the uninitiated, schemas allow you to define what
objects/resources/models your API will return. It uses JSON Schema
(https://json-schema.org/) for this.

For example:

```yml
components:
schemas:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
```

This means that any `Pet` objects will have at least an id and a name,
and an optional `tag`.

Currently the logic to display which attributes are required is wrong,
because the code expects something like:

```yml
id:
type: integer
format: int64
required: true
```

This isn't valid JSON Schema as far as I can see: https://json-schema.org/understanding-json-schema/reference/object.html#required-properties

This commit fixes the bug by making the check look at the `required`
array in the schema, rather than the property.
The OpenAPI specification dictates that that an empty or missing server
object should default to a server object of one item with a URL of "/"
[1].

This is reflected in 0.8 of openapi3_parser so the code here is adapted
to reflect that.

[1]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#openapi-object
0.8 is no longer the most recent release.
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.

3 participants