public.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. from operator import itemgetter
  2. import re
  3. import six
  4. from flask import current_app, request
  5. from flask_classful import FlaskView, route
  6. from flask_json import as_json
  7. from flexmeasures.api.common.responses import request_processed
  8. class ServicesAPI(FlaskView):
  9. route_base = "/api/v3_0"
  10. trailing_slash = False
  11. @route("", methods=["GET"])
  12. @as_json
  13. def index(self):
  14. """API endpoint to get a service listing for this version.
  15. .. :quickref: Public; Obtain a service listing for this version
  16. :resheader Content-Type: application/json
  17. :status 200: PROCESSED
  18. """
  19. services = []
  20. for rule in current_app.url_map.iter_rules():
  21. url = rule.rule
  22. if url.startswith(self.route_base):
  23. methods: str = "/".join(
  24. [m for m in rule.methods if m not in ("OPTIONS", "HEAD")]
  25. )
  26. stripped_url = removeprefix(url, self.route_base)
  27. full_url = (
  28. removesuffix(request.url_root, "/") + url
  29. if url.startswith("/")
  30. else request.url_root + url
  31. )
  32. quickref = quickref_directive(
  33. current_app.view_functions[rule.endpoint].__doc__
  34. )
  35. services.append(
  36. dict(
  37. url=full_url,
  38. name=f"{methods} {stripped_url}",
  39. description=quickref,
  40. )
  41. )
  42. response = dict(
  43. services=sorted(services, key=itemgetter("url")),
  44. version="3.0",
  45. )
  46. d, s = request_processed()
  47. return dict(**response, **d), s
  48. def quickref_directive(content):
  49. """Adapted from sphinxcontrib/autohttp/flask_base.py:quickref_directive."""
  50. rcomp = re.compile(r"^\s*.. :quickref:\s*(?P<quick>.*)$")
  51. if isinstance(content, six.string_types):
  52. content = content.splitlines()
  53. description = ""
  54. for line in content:
  55. qref = rcomp.match(line)
  56. if qref:
  57. quickref = qref.group("quick")
  58. parts = quickref.split(";", 1)
  59. if len(parts) > 1:
  60. description = parts[1].lstrip(" ")
  61. else:
  62. description = quickref
  63. break
  64. return description
  65. def removeprefix(text: str, prefix: str) -> str:
  66. """Remove a prefix from a text.
  67. todo: use text.removeprefix(prefix) instead of this method, after dropping support for Python 3.8
  68. See https://docs.python.org/3.9/library/stdtypes.html#str.removeprefix
  69. """
  70. if text.startswith(prefix):
  71. return text[len(prefix) :]
  72. else:
  73. return text
  74. def removesuffix(text: str, suffix: str) -> str:
  75. """Remove a suffix from a text.
  76. todo: use text.removesuffix(suffix) instead of this method, after dropping support for Python 3.8
  77. See https://docs.python.org/3.9/library/stdtypes.html#str.removesuffix
  78. """
  79. if text.endswith(suffix):
  80. return text[: -len(suffix)]
  81. else:
  82. return text