Skip to content

Commit 9bf250a

Browse files
nhoeningMuhammad-Moiz626joshuaunityFlix6x
authored
docs: OpenAPI/SwaggerUI (#1703)
* first endpoint working to the point that it can be executed via SwaggerUI Signed-off-by: Nicolas Höning <nicolas@seita.nl> * Added docs for Sensor endpoints. * Added SensorKwargsSchema, Fixed Direct list response, Fixed file upload issues. * Added generate-openapi to update-docs. * Updated the URL for docs. * Added docs to menu. * Removed JSON file entry. * Fetch Flexmeasures version dynamically. * Added external link and one asset endpoint for linking with sensor schedule api. * Updated changelog. * Added packages to requirements. * Update flexmeasures/api/v3_0/assets.py Co-authored-by: Nicolas Höning <nicolas@seita.nl> Signed-off-by: Muhammad-Moiz626 <muhammadmoiz4843@gmail.com> * Update flexmeasures/api/v3_0/assets.py Co-authored-by: Nicolas Höning <nicolas@seita.nl> Signed-off-by: Muhammad-Moiz626 <muhammadmoiz4843@gmail.com> * Fixed Note UI, External linking to actual flexmeasures docs, OpenAPI docs fixes. * Created FlexContext and FlexModel schemas and added to a new schema for AssetTriggerOpenAPISchema. * Documented users endpoints. * Utility function to convert non compatible fields to OpenAPI compatible string field. * Added UserId schema and fixed password-reset swagger bug. * Created Schemas for kwargs to be used with docs and actual routes, Generic pagination schemas and some changes in the users endpoints. * Complete documentation for assets endpoints. * Fixed assets page 422 error. * Fixed user endpoint bugs. * remove description for replacement string field, should resolve error as it is not supported Signed-off-by: Nicolas Höning <nicolas@seita.nl> * fix various things in sensor resource endpoints YAML (not 100%) complete yet. Signed-off-by: Nicolas Höning <nicolas@seita.nl> * better minimal scheduling example, with a fixed price Signed-off-by: Nicolas Höning <nicolas@seita.nl> * clarity on how the query parameters to the get-data endpoint need to be escaped, and can alsp be part of the JSON body Signed-off-by: Nicolas Höning <nicolas@seita.nl> * move generate-opeapi Make target to correct section Signed-off-by: Nicolas Höning <nicolas@seita.nl> * no need in teasts to assume each audit log entry happens only once Signed-off-by: Nicolas Höning <nicolas@seita.nl> * fix asset index API, some refactoring Signed-off-by: Nicolas Höning <nicolas@seita.nl> * read_only is not valid in schema fields Signed-off-by: Nicolas Höning <nicolas@seita.nl> * docs: update get asset api docs Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * docs: update api docs for triggering schedules Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * docs: initiate first accounts docs as well as schema for pagination schema Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * a few text improvements on asseet resource Signed-off-by: Nicolas Höning <nicolas@seita.nl> * docs: migrate all accounts API docs to Swagger UI Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * Update flexmeasures/api/v3_0/assets.py Co-authored-by: Nicolas Höning <nicolas@seita.nl> Signed-off-by: JDev <45713692+joshuaunity@users.noreply.github.com> * Update flexmeasures/api/v3_0/assets.py Co-authored-by: Nicolas Höning <nicolas@seita.nl> Signed-off-by: JDev <45713692+joshuaunity@users.noreply.github.com> * Update flexmeasures/api/v3_0/assets.py Co-authored-by: Nicolas Höning <nicolas@seita.nl> Signed-off-by: JDev <45713692+joshuaunity@users.noreply.github.com> * Update flexmeasures/api/v3_0/assets.py Co-authored-by: Nicolas Höning <nicolas@seita.nl> Signed-off-by: JDev <45713692+joshuaunity@users.noreply.github.com> * chore: update some asset APi doc Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * refactor: schema for asset API(set default view) example Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * Update flexmeasures/api/v3_0/accounts.py Co-authored-by: Nicolas Höning <nicolas@seita.nl> Signed-off-by: JDev <45713692+joshuaunity@users.noreply.github.com> * chore: schema syncing on accoutn API docs Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * Fix: broken schema registration Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * chore: outline list of options for user asset page default view Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * Fix: fix faiing schema rendering for create asset api Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * chore: update description for API for downloading asset chart Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * Fix: Fixed broken schema option for account API patch swagger schema Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * chore: move documentation requirements Signed-off-by: F.N. Claessen <felix@seita.nl> * Make the Sphinx/RTD part of the v3 API pretty again, using OpenAPI specs. TODO: re-create qref links Signed-off-by: Nicolas Höning <nicolas@seita.nl> * (re-)add qrefs to asset endpoints, so Sphinx shows a nice table again Signed-off-by: Nicolas Höning <nicolas@seita.nl> * chore: migrate requestAuthToken api to swagger UI/openapi Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * Add quickrefs to all endpoints we worked on so far, so the table in Sphinx/RTD is grouped nicely Signed-off-by: Nicolas Höning <nicolas@seita.nl> * chore: migrate health and version list api docs Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * Sphinx/RTD uses the same json specs as Swagger (just keep one of them in the repo is better), plus some small changes and comments Signed-off-by: Nicolas Höning <nicolas@seita.nl> * update dependencies across Python versions Signed-off-by: Nicolas Höning <nicolas@seita.nl> * move apispec requirements in the function for docs generation Signed-off-by: Nicolas Höning <nicolas@seita.nl> * docs: udpate docs qith quickrefs Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * mention OpenAPI/Swagger on API docs Signed-off-by: Nicolas Höning <nicolas@seita.nl> * Fix: fixed failing test due to circular import Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * doc: udpated docs for API to upload graph data Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * chore: discarding unused code Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * some regroupings, fixing of descriptions Signed-off-by: Nicolas Höning <nicolas@seita.nl> * chore: udpate docs Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * chore: remove default value for page query on API pagination quaries Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * Refactor: adapt schedule tests to match new cli iput format Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> * use higher Postgres version for GitHub Actions, trying to reproduce errors Signed-off-by: Nicolas Höning <nicolas@seita.nl> * ignore the new specs-generation util file in doctesting Signed-off-by: Nicolas Höning <nicolas@seita.nl> * move the OpenAPI generation script to a non-packaged script directory Signed-off-by: Nicolas Höning <nicolas@seita.nl> * Update flexmeasures/api/common/schemas/generic_schemas.py Signed-off-by: Nicolas Höning <iam@nicolashoening.de> --------- Signed-off-by: Nicolas Höning <nicolas@seita.nl> Signed-off-by: Muhammad-Moiz626 <muhammadmoiz4843@gmail.com> Signed-off-by: joshuaunity <oghenerobojosh01@gmail.com> Signed-off-by: JDev <45713692+joshuaunity@users.noreply.github.com> Signed-off-by: F.N. Claessen <felix@seita.nl> Signed-off-by: Nicolas Höning <iam@nicolashoening.de> Co-authored-by: Muhammad-Moiz626 <muhammadmoiz4843@gmail.com> Co-authored-by: joshuaunity <oghenerobojosh01@gmail.com> Co-authored-by: JDev <45713692+joshuaunity@users.noreply.github.com> Co-authored-by: F.N. Claessen <felix@seita.nl>
1 parent b7bc98e commit 9bf250a

40 files changed

+7093
-1715
lines changed

.github/workflows/lint-and-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ jobs:
8585
# Label used to access the service container
8686
postgres:
8787
# Docker Hub image
88-
image: postgres:12.5
88+
image: postgres:17.4
8989
env:
9090
POSTGRES_USER: flexmeasures_test
9191
POSTGRES_PASSWORD: flexmeasures_test

.vscode/spellright.dict

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,3 +292,8 @@ asc
292292
desc
293293
autoflush
294294
fromisoformat
295+
upsample
296+
localhost
297+
schemas
298+
curtailable
299+
embeddable

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ HIGHS_DIR = "../HiGHS"
55

66
# Note: use tabs
77
# actions which are virtual, i.e. not a script
8-
.PHONY: install install-for-dev install-for-test install-deps install-flexmeasures test freeze-deps upgrade-deps update-docs update-docs-pdf show-file-space show-data-model clean-db cli-autocomplete build-highs-macos install-highs-macos
8+
.PHONY: install install-for-dev install-for-test install-deps install-flexmeasures test freeze-deps upgrade-deps update-docs update-docs-pdf generate-openapi show-file-space show-data-model clean-db cli-autocomplete build-highs-macos install-highs-macos
99

1010

1111
# ---- Development ---
@@ -21,8 +21,10 @@ gen_code_docs := False # by default code documentation is not generated
2121
update-docs:
2222
@echo "Creating docs environment ..."
2323
make install-docs-dependencies
24+
make generate-openapi
2425
@echo "Creating documentation ..."
2526
export FLEXMEASURES_ENV=documentation; export FLEXMEASURES_PLUGINS=; export GEN_CODE_DOCS=${gen_code_docs}; cd documentation; make clean; make html SPHINXOPTS="-W --keep-going -n"; cd ..
27+
sed -i 's/(id)/id/g' flexmeasures/ui/static/documentation/html/api/v3_0.html # make sphinxcontrib-httpdomain links point to openapi-sphinx links
2628

2729
update-docs-pdf:
2830
@echo "NOTE: PDF documentation requires packages (on Debian: latexmk texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended)"
@@ -31,6 +33,10 @@ update-docs-pdf:
3133
@echo "Creating documentation ..."
3234
export FLEXMEASURES_ENV=documentation; export FLEXMEASURES_PLUGINS=; export GEN_CODE_DOCS=${gen_code_docs}; cd documentation; make clean; make latexpdf; make latexpdf; cd .. # make latexpdf can require two passes
3335

36+
generate-openapi:
37+
@echo "Generating OpenAPI specifications..."
38+
python flexmeasures/api/scripts/generate_open_api_specs.py
39+
3440
# ---- Installation ---
3541

3642
install: install-deps install-flexmeasures

documentation/api/change_log.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ API change log
55

66
.. note:: The FlexMeasures API follows its own versioning scheme. This is also reflected in the URL (e.g. `/api/v3_0`), allowing developers to upgrade at their own pace.
77

8+
9+
v3.0-28 | 2025-10-XX
10+
""""""""""""""""""""
11+
- Moved documentation to OpenAPI standard (incl. smaller improvements), each instance now provides a Swagger UI.
12+
813
v3.0-27 | 2025-09-16
914
""""""""""""""""""""
1015
- Fix schema validation in ``PATCH /assets/<id>``.

documentation/api/v3_0.rst

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,42 @@
11
.. _v3_0:
22

33
Version 3.0
4-
===========
4+
================
55

66
Summary
77
-------
88

9+
A quick overview of the available endpoints. For more details, click their names.
10+
11+
.. The qrefs make links very similar to the openapi plugin, but we have to run a sed command after the fact to make them exactly alike (see Makefile)
912
.. qrefflask:: flexmeasures.app:create(env="documentation")
1013
:modules: flexmeasures.api, flexmeasures.api.v3_0.assets, flexmeasures.api.v3_0.sensors, flexmeasures.api.v3_0.accounts, flexmeasures.api.v3_0.users, flexmeasures.api.v3_0.health, flexmeasures.api.v3_0.public
1114
:order: path
1215
:include-empty-docstring:
1316

17+
18+
OpenAPI & SwaggerUI
19+
-------------------
20+
21+
We also provide interactive SwaggerUI documentation with each instance at `/api/v3_0/docs`. See a screenshot below.
22+
23+
To access it, click on the book icon on the upper right or visit `https://<your-instance>/api/v3_0/docs`.
24+
If authenticated, you can try out the endpoints directly from there.
25+
26+
.. image:: https://github.com/FlexMeasures/screenshots/raw/main/swagger-ui.png
27+
:align: center
28+
29+
|
30+
31+
1432
API Details
1533
-----------
1634

17-
.. autoflask:: flexmeasures.app:create(env="documentation")
18-
:modules: flexmeasures.api, flexmeasures.api.v3_0.assets, flexmeasures.api.v3_0.sensors, flexmeasures.api.v3_0.accounts, flexmeasures.api.v3_0.users, flexmeasures.api.v3_0.health, flexmeasures.api.v3_0.public
19-
:order: path
20-
:include-empty-docstring:
35+
Below, each endpoint is described in detail.
36+
37+
.. we are using the specs which are located in the UI static folder, as that location is needed to serve it with each instance.
38+
.. openapi:: ../../flexmeasures/ui/static/openapi-specs.json
39+
:format:
40+
markdown
41+
:group:
42+
:examples:

documentation/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ New features
7272
* Improved timestamp on sensor detail page to be more friendly [see `PR #1632 <https://www.github.com/FlexMeasures/flexmeasures/pull/1632>`_]
7373
* Asset types support: new API endpoint (`GET /assets/types`), better docs and fix CLI command `flexmeasures show asset-types` [see `PR #1663 <https://github.com/FlexMeasures/flexmeasures/pull/1663>`_]
7474
* Add `--combine-legend` option to flexmeasures show chart. When used, all legends are combined and displayed at the bottom of the chart. If not used (default = False), each chart will display its own separate legend (as on the asset page) [see `PR #1696 <https://github.com/FlexMeasures/flexmeasures/pull/1696>`_]
75+
* Add support for using Swagger to generate API documentation [see `PR #1703 <https://github.com/FlexMeasures/flexmeasures/pull/1703>`_]
7576

7677
Infrastructure / Support
7778
----------------------

documentation/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"sphinxcontrib.autohttp.flask",
5656
"sphinxcontrib.autohttp.flaskqref",
5757
"sphinxcontrib.mermaid",
58+
"sphinxcontrib.openapi",
5859
]
5960

6061
autodoc_default_options = {}

flexmeasures/api/__init__.py

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from flask_security.utils import verify_password
88
from flask_json import as_json
99
from flask_login import current_user
10+
from webargs.flaskparser import use_args
1011
from sqlalchemy.exc import IntegrityError
1112
from sqlalchemy import select
1213

@@ -19,29 +20,46 @@
1920
)
2021
from flexmeasures.api.common.responses import invalid_sender
2122
from flexmeasures.data.schemas.utils import FMValidationError
23+
from flexmeasures.api.v3_0.users import AuthRequestSchema
2224

2325
# The api blueprint. It is registered with the Flask app (see app.py)
2426
flexmeasures_api = Blueprint("flexmeasures_api", __name__)
2527

2628

2729
@flexmeasures_api.route("/requestAuthToken", methods=["POST"])
30+
@use_args(AuthRequestSchema, location="json")
2831
@as_json
29-
def request_auth_token():
30-
"""API endpoint to get a fresh authentication access token. Be aware that this fresh token has a limited lifetime
31-
(which depends on the current system setting SECURITY_TOKEN_MAX_AGE).
32-
33-
Pass the `email` parameter to identify the user.
34-
Pass the `password` parameter to authenticate the user (if not already authenticated in current session)
35-
36-
.. :quickref: Public; Obtain an authentication token
37-
"""
38-
32+
def request_auth_token(args) -> tuple[dict, int]:
3933
"""
40-
The login of flask security returns the auth token, as well, but we'd like to
41-
* skip authentication if the user is authenticated already
42-
* be exempt from csrf protection (this is a JSON-only endpoint)
43-
* use a more fitting name inside the api namespace
44-
* return the information in a nicer structure
34+
.. :quickref: Auth; Obtain authentication token
35+
---
36+
post:
37+
summary: Obtain a fresh authentication access token.
38+
description: |
39+
Retrieve a short-lived authentication token using email and password. The lifetime of this token depends on the current system setting
40+
SECURITY_TOKEN_MAX_AGE.
41+
42+
requestBody:
43+
required: true
44+
content:
45+
application/json:
46+
schema: AuthRequestSchema
47+
responses:
48+
200:
49+
description: Token successfully generated
50+
content:
51+
application/json:
52+
schema:
53+
type: object
54+
properties:
55+
authentication_token:
56+
type: string
57+
400:
58+
description: INVALID_REQUEST
59+
401:
60+
description: UNAUTHORIZED
61+
tags:
62+
- Auth
4563
"""
4664
try:
4765
if not request.is_json:
@@ -80,15 +98,45 @@ def request_auth_token():
8098
@flexmeasures_api.route("/", methods=["GET"])
8199
@as_json
82100
def get_versions() -> dict:
83-
"""Public endpoint to list API versions.
84-
101+
"""
85102
.. :quickref: Public; List available API versions
86-
103+
---
104+
get:
105+
summary: List available API versions
106+
description: |
107+
Public endpoint to list all available FlexMeasures API versions.
108+
109+
This can be used to programmatically discover which API versions
110+
are currently supported and their base URLs.
111+
responses:
112+
200:
113+
description: Successfully retrieved available API versions.
114+
content:
115+
application/json:
116+
schema:
117+
type: object
118+
properties:
119+
message:
120+
type: string
121+
example: "For these API versions a public endpoint is available, listing its service."
122+
versions:
123+
type: array
124+
items:
125+
type: string
126+
example: ["v3_0"]
127+
flexmeasures_version:
128+
type: string
129+
example: "0.18.3"
130+
tags:
131+
- Public
87132
"""
133+
88134
response = {
89-
"message": "For these API versions a public endpoint is available, listing its service. For example: "
90-
"/api/v3_0. An authentication token can be requested at: "
91-
"/api/requestAuthToken",
135+
"message": (
136+
"For these API versions a public endpoint is available, listing its service. For example: "
137+
"/api/v3_0. An authentication token can be requested at: "
138+
"/api/requestAuthToken"
139+
),
92140
"versions": ["v3_0"],
93141
"flexmeasures_version": flexmeasures_version,
94142
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from marshmallow import fields, validate
2+
3+
from flexmeasures.api.common.schemas.generic_schemas import PaginationSchema
4+
from flexmeasures.api.common.schemas.users import AccountIdField
5+
6+
7+
class AssetAPIQuerySchema(PaginationSchema):
8+
sort_by = fields.Str(
9+
required=False,
10+
validate=validate.OneOf(["id", "name", "owner"]),
11+
)
12+
account = AccountIdField(data_key="account_id", load_default=None)
13+
all_accessible = fields.Bool(data_key="all_accessible", load_default=False)
14+
include_public = fields.Bool(data_key="include_public", load_default=False)
15+
16+
17+
class AssetPaginationSchema(PaginationSchema):
18+
sort_by = fields.Str(
19+
required=False,
20+
validate=validate.OneOf(["id", "name", "resolution"]),
21+
)
22+
sort_dir = fields.Str(
23+
required=False,
24+
validate=validate.OneOf(["asc", "desc"]),
25+
)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from marshmallow import Schema, fields, validate
2+
from flexmeasures.api.common.schemas.search import SearchFilterField
3+
4+
5+
class PaginationSchema(Schema):
6+
# note: the absence of this parameter would signal to the API to not paginate (so there is no default set here)
7+
page = fields.Int(required=False, validate=validate.Range(min=1))
8+
per_page = fields.Int(
9+
required=False, validate=validate.Range(min=1), load_default=10
10+
)
11+
filter = SearchFilterField(required=False)
12+
sort_by = fields.Str(
13+
required=False,
14+
)
15+
sort_dir = fields.Str(required=False, validate=validate.OneOf(["asc", "desc"]))

0 commit comments

Comments
 (0)