snyk / vervet

API resource versioning tool
Other
19 stars 5 forks source link

OAS extension supporting external documentation references #85

Open cmars opened 2 years ago

cmars commented 2 years ago

Add support for including free-form markdown in API documentation.

We want the compiled OpenAPI spec to provide everything needed to generate API documentation. However, we also don't want to have to author this content in a large JSON string with escaped newlines in an actual OpenAPI spec.

Instead, what we want is the ability to reference standalone markdown files, so that they are included in the compiled OpenAPI spec (possibly as large JSON strings), which may then be processed by an API documentation SPA or site generator.

So, I'm proposing an extension, x-snyk-doc-content. An example:

x-snyk-doc-content:
  intent: header
  format: markdown
  $ref: "/path/to/header.md"

What these properties mean:

As an alternative to $ref:, contents: contains the literal documentation contents inline as a string. In YAML, this can be expressed with a multiline string, contents: |- for example. In JSON, well, it's a large newline-escaped monster of a quoted string.

When Vervet localizes an OpenAPI spec, it should replace the $ref: properties with contents: in this extension. This will require some custom implementation, as the core kin-openapi doesn't do this for us with made-up extensions. However, it is possible -- see the implementation of vervet.IncludeHeaders for an example of resolving custom extension $refs.

jcsackett commented 2 years ago

Can extension fields be added to any part of the schema? Because if so target seems unnecessary--better to have the ref with the associated part of the schema and whatever processor decides from there.

If not, I think target ought to be more specific to something within the rest of the openapi spec--it seems peculiar to include a target hint for e.g. RapiDoc slots, since that's an implementation detail someone updating docs in the app shouldn't need to know/think about.

Otherwise I really dig this idea.

cmars commented 2 years ago

If we make the extension purely contextual and remove target: -- so that it applies to its parent element -- is it expressive enough to allow for including content that goes into a page header, extra sidebar sections, etc.? All of these things might go in the top-level of the OpenAPI spec, but how does the docs processor then know where to place the content?

Maybe the answer is that we use both; we use target: (is there now a more apt name for this?) as an abstract locator or hint that can be used by the docs processor to decide where to put things, and use the extension parent to determine the scope? So in that case, target: doesn't map 1:1 with RapiDoc tags, but tells a docs processor "this is what this chunk of content is intended to be used for" and the processor decides where to place it in the context of where it occurs in the OpenAPI doc.

jcsackett commented 2 years ago

Maybe the answer is that we use both; we use target: (is there now a more apt name for this?) as an abstract locator or hint that can be used by the docs processor to decide where to put things, and use the extension parent to determine the scope? So in that case, target: doesn't map 1:1 with RapiDoc tags, but tells a docs processor "this is what this chunk of content is intended to be used for" and the processor decides where to place it in the context of where it occurs in the OpenAPI doc.

Ok, this seems like a reasonable field then, and I think you already named it when you said what it would be for: intent. Sound good?

cmars commented 2 years ago

One thing we should also make clear is when to use this extension, vs. existing features in OpenAPI. In some cases, a description: might be enough. This should be used sparingly, when it is not enough.

cmars commented 2 years ago

This can be a linting rule; only allow use of the extension if a non-empty description is provided.