Skip to content

Commit

Permalink
Save changes
Browse files Browse the repository at this point in the history
  • Loading branch information
jamespfennell committed Jul 3, 2020
1 parent c55c486 commit 14d74cf
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 24 deletions.
40 changes: 27 additions & 13 deletions docs/api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import dataclasses
import inspect

import typing
from transiter.http import endpoints, flaskapp
from transiter.http import httpmanager
from transiter.http.flaskapp import app
Expand All @@ -9,12 +9,20 @@
@dataclasses.dataclass
class Group:
name: str
module: str
module: typing.Any
extra_modules: list = dataclasses.field(default_factory=list)
endpoints: list = dataclasses.field(default_factory=list)
extra_endpoints: list = dataclasses.field(default_factory=list)

def page(self):
return f"api/{self.name}.md".replace(" ", "-").lower()

def all_modules(self):
return [self.module] + self.extra_modules

def all_endpoints(self):
return self.endpoints + self.extra_endpoints


@dataclasses.dataclass
class Endpoint:
Expand All @@ -26,7 +34,7 @@ class Endpoint:


groups = [
Group("Entrypoint", flaskapp),
Group("Entrypoint", flaskapp, [endpoints.docsendpoints]),
Group("Systems", endpoints.systemendpoints),
Group("Stops", endpoints.stopendpoints),
Group("Routes", endpoints.routeendpoints),
Expand All @@ -40,12 +48,8 @@ class Endpoint:

def match_group(endpoint, endpoint_module):
for group in groups:
if isinstance(group.module, str):
if group.module == endpoint[: len(group.module)]:
return group
else:
if endpoint_module is group.module:
return group
if endpoint_module in group.all_modules():
return group
return None


Expand All @@ -66,7 +70,7 @@ def clean_doc(raw_doc):
def populate_endpoints():
func_to_rule = {}
for rule in app.url_map.iter_rules():
if rule.rule[-1] == "/" and rule.rule != "/":
if rule.rule[-1] == "/" and rule.rule != "/" and rule.rule[:5] != "/docs":
continue
func_to_rule[app.view_functions[rule.endpoint]] = rule

Expand All @@ -75,6 +79,7 @@ def populate_endpoints():
if rule is None:
print("Skipping")
continue
del func_to_rule[endpoint.func]
group = match_group(rule.endpoint, inspect.getmodule(endpoint.func))
if group is None:
print(f"Warning: no group for {rule.endpoint}, skipping ")
Expand All @@ -95,7 +100,11 @@ def populate_endpoints():
)
continue

group.endpoints.append(
if group.module is inspect.getmodule(endpoint.func):
add_to = group.endpoints
else:
add_to = group.extra_endpoints
add_to.append(
Endpoint(
title=title,
rule=rule.rule,
Expand All @@ -105,6 +114,11 @@ def populate_endpoints():
)
)

for func, rule in func_to_rule.items():
print(
f"The function {func} is registered as a flask route but has no documentation"
)


def calculate_method(methods):
for method in ["GET", "POST", "PUT", "DELETE"]:
Expand Down Expand Up @@ -142,7 +156,7 @@ def build_quick_reference_row(endpoint: Endpoint, page: str):
print("----------|-------------", file=f)
for group in groups:
print(f"**{group.name} endpoints**", file=f)
for endpoint in group.endpoints:
for endpoint in group.all_endpoints():
print(build_quick_reference_row(endpoint, group.page()[4:]), file=f)

for group in groups:
Expand All @@ -158,7 +172,7 @@ def build_quick_reference_row(endpoint: Endpoint, page: str):
else:
print("Warning: module refered to as string")
print(f"# {group.name}", file=f)
for endpoint in group.endpoints:
for endpoint in group.all_endpoints():
print("", file=f)
print(f"## {endpoint.title}", file=f)
print("", file=f)
Expand Down
12 changes: 12 additions & 0 deletions docs/docs/api/agencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,15 @@


Doc

## List agencies in a system

`GET /systems/<system_id>/agencies`



## Get an agency in a system

`GET /systems/<system_id>/agencies/<agency_id>`


20 changes: 17 additions & 3 deletions docs/docs/api/entrypoint.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@

# This module contains the actual Flask app and is a such the 'root' of the HTTP
# Entrypoint and docs

server. All other HTTP endpoints are linked to the app via blueprints in this
module.


## HTTP API entry point

`GET /`


Provides basic information about this Transiter instance and the Transit
systems it contains.

## Internal documentation

`GET /docs/<path:path>`


More details
4 changes: 4 additions & 0 deletions docs/docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ by clicking any of the example links below.
Operation | API endpoint
----------|-------------
**Entrypoint endpoints**
[HTTP API entry point](entrypoint.md#http-api-entry-point) | `GET /`
[Internal documentation](entrypoint.md#internal-documentation) | `GET /docs/<path:path>`
**Systems endpoints**
[List all systems](systems.md#list-all-systems) | `GET /systems`
[Get a specific system](systems.md#get-a-specific-system) | `GET /systems/<system_id>`
Expand All @@ -31,6 +33,8 @@ Operation | API endpoint
[List trips in a route](trips.md#list-trips-in-a-route) | `GET /systems/<system_id>/routes/<route_id>/trips`
[Get a trip in a route](trips.md#get-a-trip-in-a-route) | `GET /systems/<system_id>/routes/<route_id>/trips/<trip_id>`
**Agencies endpoints**
[List agencies in a system](agencies.md#list-agencies-in-a-system) | `GET /systems/<system_id>/agencies`
[Get an agency in a system](agencies.md#get-an-agency-in-a-system) | `GET /systems/<system_id>/agencies/<agency_id>`
**Feeds endpoints**
[List feeds in a system](feeds.md#list-feeds-in-a-system) | `GET /systems/<system_id>/feeds`
[Get a feed in a system](feeds.md#get-a-feed-in-a-system) | `GET /systems/<system_id>/feeds/<feed_id>`
Expand Down
6 changes: 6 additions & 0 deletions transiter/http/endpoints/agencyendpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
@http_endpoint(agency_endpoints, "")
@link_target(views.AgenciesInSystem, ["_system_id"])
def list_all_in_system(system_id):
"""
List agencies in a system
"""
return agencyservice.list_all_in_system(
system_id,
alerts_detail=get_enum_url_parameter("alerts_detail", views.AlertsDetail),
Expand All @@ -27,6 +30,9 @@ def list_all_in_system(system_id):
@http_endpoint(agency_endpoints, "/<agency_id>")
@link_target(views.Agency, ["_system_id", "id"])
def get_in_system_by_id(system_id, agency_id):
"""
Get an agency in a system
"""
return agencyservice.get_in_system_by_id(
system_id,
agency_id,
Expand Down
9 changes: 8 additions & 1 deletion transiter/http/endpoints/docsendpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from transiter import config, exceptions
from transiter.http import httpviews
from transiter.http.httpmanager import link_target, HttpStatus
from transiter.http.httpmanager import link_target, HttpStatus, register_documented_endpoint

docs_endpoints = flask.Blueprint(__name__, __name__)

Expand Down Expand Up @@ -47,9 +47,16 @@
# links. We need to redirect onto the URL with the slash in order for these to work.
@docs_endpoints.route("/", strict_slashes=True)
@docs_endpoints.route("/<path:path>", strict_slashes=True)
@register_documented_endpoint(None, "GET")
@link_target(httpviews.InternalDocumentationLink)
def docs(path="index.html", retry_with_index_dot_html=True, perform_validation=True):
"""
Internal documentation
More details
"""
if not config.DOCUMENTATION_ENABLED:
logger.debug(f"Documentation not enabled so returning a 404 for docs/{path}.")
flask.abort(HttpStatus.NOT_FOUND)
if perform_validation and not _documentation_root_is_valid():
logger.error(
Expand Down
6 changes: 2 additions & 4 deletions transiter/http/flaskapp.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
"""
This module contains the actual Flask app and is a such the 'root' of the HTTP
server. All other HTTP endpoints are linked to the app via blueprints in this
module.
Entrypoint and docs
"""
import datetime
import logging
Expand Down Expand Up @@ -83,7 +81,7 @@ def method_not_allowed(werkzeug_exception: werkzeug_exceptions.MethodNotAllowed)

@http_endpoint(app, "/")
def root():
"""HTTP/REST API entry point.
"""HTTP API entry point
Provides basic information about this Transiter instance and the Transit
systems it contains.
Expand Down
6 changes: 3 additions & 3 deletions transiter/http/httpmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def http_endpoint(
appropriate HTTP headers
"""
decorators = [
_register_documented_endpoint(flask_rule, method.value),
register_documented_endpoint(flask_rule, method.value),
flask_entity.route(flask_rule + "/", methods=[method.value]),
flask_entity.route(flask_rule, methods=[method.value]),
]
Expand All @@ -101,7 +101,7 @@ def composed_decorator(func):

@dataclasses.dataclass
class _DocumentedEndpoint:
rule: str
rule: str # TODO; remove, unused
method: str
func: str

Expand All @@ -121,7 +121,7 @@ def get_documented_endpoints():
return _documented_endpoints


def _register_documented_endpoint(flask_rule, method):
def register_documented_endpoint(flask_rule, method):
def decorator_(func):
_documented_endpoints.append(
_DocumentedEndpoint(rule=flask_rule, method=method, func=func,)
Expand Down

0 comments on commit 14d74cf

Please sign in to comment.