Skip to content

Commit

Permalink
v1.0.0 ✨
Browse files Browse the repository at this point in the history
  • Loading branch information
RobertoPrevato committed Apr 20, 2022
1 parent 05c2361 commit 3090439
Show file tree
Hide file tree
Showing 80 changed files with 16,588 additions and 23 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.6, 3.7, 3.8, 3.9, "3.10"]
python-version: [3.7, 3.8, 3.9, "3.10"]

steps:
- uses: actions/checkout@v1
Expand Down Expand Up @@ -48,7 +48,7 @@ jobs:
run: |
PYVER=`python -V 2>&1`
if [ "$PYVER" == "Python 3.10.0" ]; then
if [ "${PYVER:0:-2}" == "Python 3.10" ]; then
pip install -r requirements.txt
else
pip install -U --no-index --find-links=deps deps/*
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - 2022-04-20 :sparkles:
- Adds features and a CLI to generate artifacts from OpenAPI Documentation
files (markdown for MkDocs and PyMdown extensions, PlantUML class diagrams
from components schemas)
- Drops support for Python 3.6

## [0.1.6] - 2021-11-17 :gem:
- Adds `py.typed` file
- Add `Python 3.10` to the GitHub Workflow
Expand All @@ -27,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Corrects a bug forcing `camelCase` on examples objects handled as dataclasses
- Adds base64 ValueFormat to the v3 enum

## [0.1.2] - 2021-05-03 :notes:

- Adds a changelog
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2020 Neoteroi
Copyright (c) 2022 Neoteroi

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
6 changes: 6 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
include LICENSE
include README.md
recursive-include openapidocs *.html *.md

# Stubs
include openapidocs/py.typed
93 changes: 83 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,105 @@
![Build](https://github.com/Neoteroi/essentials-openapi/workflows/Build/badge.svg)
[![pypi](https://img.shields.io/pypi/v/essentials-openapi.svg)](https://pypi.python.org/pypi/essentials-openapi)
[![versions](https://img.shields.io/pypi/pyversions/essentials-openapi.svg)](https://github.com/neoteroi/essentials-openapi)
[![license](https://img.shields.io/github/license/neoteroi/essentials-openapi.svg)](https://github.com/neoteroi/essentials-openapi/blob/master/LICENSE)
[![license](https://img.shields.io/github/license/neoteroi/essentials-openapi.svg)](https://github.com/neoteroi/essentials-openapi/blob/main/LICENSE)
[![codecov](https://codecov.io/gh/Neoteroi/essentials-openapi/branch/main/graph/badge.svg?token=WEZ8YECJDF)](https://codecov.io/gh/Neoteroi/essentials-openapi)

# essentials-openapi

Classes to generate OpenAPI Documentation v3 and v2, in JSON and YAML.
Classes to generate [OpenAPI Documentation](https://swagger.io/specification/)
v3 and v2, in JSON and YAML, and to generate other kinds of documents from
OpenAPI Documentation files.

```bash
pip install essentials-openapi
```

To install with dependencies to generate other kinds of artifacts from source
OpenAPI Documentation files:

```bash
pip install essentials-openapi[full]
```

## Useful links

* https://swagger.io/specification/
* https://editor.swagger.io

## Usage
This library has been created to implement generation of OpenAPI Documentation
This library has been originally created to implement generation of OpenAPI Documentation
in the [`BlackSheep` web framework](https://github.com/RobertoPrevato/BlackSheep).
This package contains only parts that belong logically to the OpenAPI specification,
and can be reused for other applications.
However, this package is abstracted from that web framework and can be reused for other
applications. Today this library also offers functions to generate documentation from
source OpenAPI Documentation files.

## Features to generate artifacts from Open API Documentation

These require the full package: install it using `pip install essentials-openapi[full]`.

To generate output for [MkDocs](https://www.mkdocs.org) and [PyMdown extentions](https://facelessuser.github.io/pymdown-extensions/):

```bash
oad gen-docs -s example1-openapi.json -d output.md
```

![Example MkDocs documentation](./docs/example-1.png)

_Example of MkDocs documentation generated using [Neoteroi/mkdocs-plugins](https://github.com/Neoteroi/mkdocs-plugins)._

---

To generate a [PlantUML](https://plantuml.com) [class
diagram](https://plantuml.com/class-diagram) of the components schemas:

```bash
oad gen-docs -s source-openapi.json -d schemas.wsd --style "PLANTUML_SCHEMAS"
```

![Example schemas](./docs/example-schemas.png)

_Example of PlantUML diagram generated from components schemas._

### Goals

* Provide an API to generate OpenAPI Documentation files.
* Providing functions to handle OpenAPI Documentation, like those to generate
other kinds of documentation from source OpenAPI Documentation files.
* Support enough features to be useful for the most common API scenarios,
especially for OAD files that are generated automatically from web frameworks.

### Non-Goals

* To implement the whole OAD Specification.
* For the features that generate artifacts: OpenAPI Documentation files are
**supposed to be coming from trusted sources**. Trying to handle source files
from untrusted sources and potentially causing HTML injection is out of the
scope of this library.

## Limitations

* Partial support for Parameter properties: `style` , `allow_reserved` ,
`explode` are not handled
* Doesn't implement validation of values, currently it is only concerned in
generating code from a higher level API (it might be extended in the future
with classes for validation)
* Partial support for Parameter properties: `style`, `allow_reserved`, `explode` are not
handled.
* Doesn't implement validation of values, currently it is only concerned in generating
code from a higher level API (it might be extended in the future with classes for
validation).
* The features to generate artifacts from OpenAPI Documentation currently support only
Version 3 of the specification.

### Styles

| Style | Int value | Description |
| ---------------- | --------- | -------------------------------------------- |
| MKDOCS | 1 | Markdown for MkDocs and PyMdown extensions. |
| MARKDOWN | 2 | Basic Markdown. |
| HTML | 3 | Plain HTML _(planned, not yet implemented)_. |
| PLANTUML_SCHEMAS | 100 | PlantUML schema for components schemas. |

### Supported sources for OpenAPI Documentation

| Source | Example |
| ------------------------------ | ---------------------------------------------------- |
| YAML file | `./docs/swagger.yaml` |
| JSON file | `./docs/swagger.json` |
| URL returning YAML on HTTP GET | `https://example-domain.net/swagger/v1/swagger.yaml` |
| URL returning JSON on HTTP GET | `https://example-domain.net/swagger/v1/swagger.json` |
Binary file added docs/example-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/example-schemas.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions openapidocs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VERSION = "1.0.0"
Empty file.
70 changes: 70 additions & 0 deletions openapidocs/commands/docs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from typing import Union

import click

from openapidocs.logs import logger
from openapidocs.mk.generate import generate_document
from openapidocs.mk.jinja import OutputStyle


@click.command(name="gen-docs")
@click.option(
"-s",
"--source",
help=(
"Source of the OpenAPI Documentation file. "
"This can be either a public URL or a path to a file."
),
required=True,
)
@click.option(
"-d",
"--destination",
help="Destination file path.",
required=True,
)
@click.option(
"-t",
"--style",
help="The style of the output.",
required=False,
default="MKDOCS",
show_default=True,
)
def generate_documents_command(source: str, destination: str, style: Union[int, str]):
"""
Generates other kinds of documents from source OpenAPI Documentation files.
For example, to generate Markdown for MkDocs and PyMdown:
$ openapidocs gen-docs -s source-openapi.json -d output.md
JSON and YAML sources are supported.
It is also possible to fetch the specification file from a public URL:
$ openapidocs gen-docs -s https://.../source-openapi.json -d output.md
For more information, refer to the documentation at
https://github.com/Neoteroi/essentials-openapi
"""
try:
generate_document(source, destination, style)
except KeyboardInterrupt: # pragma: nocover
logger.info("User interrupted")
exit(1)
except ValueError as value_error:
logger.error(value_error)
exit(2)


@click.command(name="list-styles")
def list_styles_command():
"""
Displays the supported output styles on the screen.
"""
try:
for value in OutputStyle:
logger.info("%s: %s", value.name, value.value)
except KeyboardInterrupt: # pragma: nocover
logger.info("User interrupted")
exit(1)
8 changes: 8 additions & 0 deletions openapidocs/logs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import logging
import logging.handlers

from rich.logging import RichHandler

logger = logging.getLogger("openapidocs")
logger.setLevel(logging.INFO)
logger.addHandler(RichHandler())
31 changes: 31 additions & 0 deletions openapidocs/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import logging
import sys

import click

from openapidocs import VERSION
from openapidocs.commands.docs import generate_documents_command, list_styles_command
from openapidocs.logs import logger

sys.path.append(".")


@click.group()
@click.option(
"--verbose", default=False, help="Whether to display debug output.", is_flag=True
)
@click.version_option(version=VERSION)
def main(verbose: bool):
"""
Essentials OpenAPI CLI.
https://github.com/Neoteroi/essentials-openapi
"""
if verbose: # pragma: nocover
logger.setLevel(logging.DEBUG)

logger.debug("Running in --verbose mode")


main.add_command(generate_documents_command)
main.add_command(list_styles_command)
58 changes: 58 additions & 0 deletions openapidocs/mk/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""
This module provides common functions to generate other kinds of representations from
source OpenAPI Documentation files.
"""
import re
from http import HTTPStatus

import markupsafe


def get_http_status_phrase(status_code) -> str:
try:
http_status = HTTPStatus(int(status_code))
return http_status.phrase
except ValueError:
return ""


def read_dict(obj, *args, default=None):
"""
Reads properties in a source dictionary, returning None if any
Example:
read_dict({"a": {"b": {"c": True}}}, "a", "b", "c") --> True
"""
assert isinstance(obj, dict)

value = obj
for part in args:
for key in part.split():
if not isinstance(value, dict):
raise ValueError(f"Invalid sub-path: {repr(args)}")

value = value.get(key)

if value is None:
return default

if value is None or value is obj:
return default

return value


def sort_dict(obj):
"""
Yields (key, value) of a dictionary with keys in alphabetical order.
"""
for key in sorted(obj, key=str.lower):
yield key, obj[key]


def highlight_params(path: str) -> str:
def replacer(match):
value = match.group()
return f'<span class="route-param">{markupsafe.escape(value)}</span>'

return re.sub(r"\{[^\}]+\}", replacer, path)
Loading

0 comments on commit 3090439

Please sign in to comment.