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

Document how clients can monitor deprecation and sunset response headers #565

Merged
merged 5 commits into from Dec 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions documentation/api/change_log.rst
Expand Up @@ -20,6 +20,8 @@ v3.0-5 | 2022-12-30
- ``production-price-sensor`` -> send in ``flex-context`` instead
- ``inflexible-device-sensors`` -> send in ``flex-context`` instead

- Added a subsection on deprecating and sunsetting to the Introduction section.
- Added a subsection on describing flexibility to the Notation section.

v3.0-4 | 2022-12-08
"""""""""""""""""""
Expand Down
20 changes: 20 additions & 0 deletions documentation/api/introduction.rst
Expand Up @@ -119,3 +119,23 @@ More information about a deprecation, sunset, and possibly recommended replaceme
- ``"latest-version"``
- ``"alternate"``
- ``"sunset"``

Here is a client-side code example in Python (this merely prints out the deprecation header, sunset header and relevant links, and should be revised to make use of the client's monitoring tools):

.. code-block:: python

def check_deprecation_and_sunset(self, url, response):
"""Print deprecation and sunset headers, along with info links.

Reference
---------
https://flexmeasures.readthedocs.io/en/latest/api/introduction.html#deprecation-and-sunset
"""
# Go through the response headers in their given order
for header, content in response.headers:
if header == "Deprecation":
print(f"Your request to {url} returned a deprecation warning. Deprecation: {content}")
elif header == "Sunset":
print(f"Your request to {url} returned a sunset warning. Sunset: {content}")
elif header == "Link" and ('rel="deprecation";' in content or 'rel="sunset";' in content):
print(f"Further info is available: {content}")
2 changes: 1 addition & 1 deletion documentation/changelog.rst
Expand Up @@ -44,7 +44,7 @@ Infrastructure / Support
* Revised strategy for removing unchanged beliefs when saving data: retain the oldest measurement (ex-post belief), too [see `PR #518 <http://www.github.com/FlexMeasures/flexmeasures/pull/518>`_]
* Scheduling test for maximizing self-consumption, and improved time series db queries for fixed tariffs (and other long-term constants) [see `PR #532 <http://www.github.com/FlexMeasures/flexmeasures/pull/532>`_]
* Clean up table formatting for ``flexmeasures show`` CLI commands [see `PR #540 <http://www.github.com/FlexMeasures/flexmeasures/pull/540>`_]
* Add ``"Deprecation"`` and ``"Sunset"`` response headers for API users of deprecated API versions, and log warnings for FlexMeasures hosts when users still use them [see `PR #554 <http://www.github.com/FlexMeasures/flexmeasures/pull/554>`_]
* Add ``"Deprecation"`` and ``"Sunset"`` response headers for API users of deprecated API versions, and log warnings for FlexMeasures hosts when users still use them [see `PR #554 <http://www.github.com/FlexMeasures/flexmeasures/pull/554>`_ and `PR #565 <http://www.github.com/FlexMeasures/flexmeasures/pull/565>`_]
* Explain how to avoid potential ``SMTPRecipientsRefused`` errors when using FlexMeasures in combination with a mail server [see `PR #558 <http://www.github.com/FlexMeasures/flexmeasures/pull/558>`_]

.. warning:: The API endpoint (`[POST] /sensors/(id)/schedules/trigger <api/v3_0.html#post--api-v3_0-sensors-(id)-schedules-trigger>`_) to make new schedules will (in v0.13) sunset the storage flexibility parameters (they move to the ``flex-model`` parameter group), as well as the parameters describing other sensors (they move to ``flex-context``).
Expand Down
11 changes: 4 additions & 7 deletions flexmeasures/api/common/utils/deprecation_utils.py
Expand Up @@ -133,22 +133,19 @@ def _add_headers(
sunset: str | None,
sunset_link: str | None,
) -> Response:
response.headers["Deprecation"] = deprecation
if sunset:
response.headers["Sunset"] = sunset
response.headers.extend({"Deprecation": deprecation})
if deprecation_link:
response = _add_link(response, deprecation_link, "deprecation")
if sunset:
response.headers.extend({"Sunset": sunset})
if sunset_link:
response = _add_link(response, sunset_link, "sunset")
return response


def _add_link(response: Response, link: str, rel: str) -> Response:
link_text = f'<{link}>; rel="{rel}"; type="text/html"'
if response.headers.get("Link"):
response.headers["Link"] += f", {link_text}"
else:
response.headers["Link"] = link_text
response.headers.extend({"Link": link_text})
return response


Expand Down
24 changes: 16 additions & 8 deletions flexmeasures/api/tests/utils.py
Expand Up @@ -114,17 +114,25 @@ def check_deprecation(
"""Check deprecation and sunset headers.

Also make sure we link to some url for further info.
If deprecation is None, make sure there are *no* deprecation headers.
If sunset is None, make sure there are *no* sunset headers.
"""
print(response.headers)
if deprecation:
assert deprecation in response.headers.get("Deprecation", [])
assert 'rel="deprecation"' in response.headers.get("Link", [])
assert deprecation in response.headers.getlist("Deprecation")
assert any(
'rel="deprecation"' in link for link in response.headers.getlist("Link")
)
else:
assert deprecation not in response.headers.get("Deprecation", [])
assert 'rel="deprecation"' not in response.headers.get("Link", [])
assert deprecation not in response.headers.getlist("Deprecation")
assert all(
'rel="deprecation"' not in link for link in response.headers.getlist("Link")
)
if sunset:
assert sunset in response.headers.get("Sunset", [])
assert 'rel="sunset"' in response.headers.get("Link", [])
assert sunset in response.headers.getlist("Sunset")
assert any('rel="sunset"' in link for link in response.headers.getlist("Link"))
else:
assert sunset not in response.headers.get("Sunset", [])
assert 'rel="sunset"' not in response.headers.get("Link", [])
assert sunset not in response.headers.getlist("Sunset")
assert all(
'rel="sunset"' not in link for link in response.headers.getlist("Link")
)