From 01e19137106fb4528d500e3faa596c91d4e54758 Mon Sep 17 00:00:00 2001 From: Eric Benson Date: Fri, 26 Jun 2020 14:30:32 -0500 Subject: [PATCH 1/2] Added ability to use suffixes with a controller that has been already registered without suffixes. --- README.md | 55 ++++++++++++++++++++++++++++++--- falcon_apispec/falcon_plugin.py | 30 +++++++++++++----- tests/falcon_test.py | 42 +++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 2278b4b..7d86d48 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ class PetSchema(Schema): # Create Falcon web app app = falcon.API() -class RandomPetResource: +class PetResource: def on_get(self, req, resp): """A cute furry animal endpoint. --- @@ -55,11 +55,30 @@ class RandomPetResource: """ pet = get_random_pet() # returns JSON resp.media = pet + + def on_get_one(self, req, resp, pet): + """A cute furry animal endpoint. + --- + description: Get a random pet + parameters: + - in: path + name: pet + required: true + schema: + type: string + responses: + 200: + description: A pet to be returned + schema: PetSchema + """ + pet = get_pet(pet) # returns JSON + resp.media = pet # create instance of resource -random_pet_resource = RandomPetResource() +pet_resource = PetResource() # pass into `add_route` for Falcon -app.add_route("/random", random_pet_resource) +app.add_route("/random", pet_resource) +app.add_route("/{pet}", pet_resource, suffix="one") # Create an APISpec @@ -77,6 +96,9 @@ spec = APISpec( spec.components.schema('Category', schema=CategorySchema) spec.components.schema('Pet', schema=PetSchema) # pass created resource into `path` for APISpec +# should be passed twice so the suffix is registered +# path should be called n + 1 times where n is the number of suffixes +spec.path(resource=random_pet_resource) spec.path(resource=random_pet_resource) ``` @@ -101,7 +123,30 @@ spec.to_dict() # }, # "description": "A pet to be returned" # } -# }, +# } +# } +# }, +# "/v1/client/{team}/{client_uuid:uuid}": { +# "get": { +# "description": "A cute furry animal endpoint.", +# "parameters": [ +# { +# "in": "path", +# "name": "pet", +# "required": true, +# "schema": { +# "type": "string" +# } +# } +# ], +# "responses": { +# "200": { +# "schema": { +# "$ref": "#/definitions/Pet" +# }, +# "description": "A pet to be returned" +# } +# } # } # } # }, @@ -133,7 +178,7 @@ spec.to_dict() # } # } # } -# }, +# } # } spec.to_yaml() diff --git a/falcon_apispec/falcon_plugin.py b/falcon_apispec/falcon_plugin.py index 9583a93..a04f10c 100644 --- a/falcon_apispec/falcon_plugin.py +++ b/falcon_apispec/falcon_plugin.py @@ -10,27 +10,43 @@ class FalconPlugin(BasePlugin): def __init__(self, app): super(FalconPlugin, self).__init__() self._app = app + self.uris_returned = [] - @staticmethod - def _generate_resource_uri_mapping(app): + def _generate_resource_uri_mapping(self, app): routes_to_check = copy.copy(app._router._roots) + if len(routes_to_check) == 0: + return {} + mapping = {} for route in routes_to_check: - uri = route.uri_template - resource = route.resource + if route.uri_template is not None and route.uri_template not in self.uris_returned: + uri = route.uri_template + resource = route.resource + method_map = route.method_map + elif route.uri_template is None and len(route.children) != 0: + routes_to_check.extend(route.children) + continue + else: + for child in route.children: + if child.uri_template not in self.uris_returned: + uri = child.uri_template + resource = child.resource + method_map = child.method_map + break + mapping[resource] = { "uri": uri, "methods": {} } - if route.method_map: - for method_name, method_handler in route.method_map.items(): + if method_map: + for method_name, method_handler in method_map.items(): if method_handler.__dict__.get("__module__") == "falcon.responders": continue mapping[resource]["methods"][method_name.lower()] = method_handler - routes_to_check.extend(route.children) + self.uris_returned.append(uri) return mapping def path_helper(self, operations, resource, base_path=None, **kwargs): diff --git a/tests/falcon_test.py b/tests/falcon_test.py index e5fadee..ca3250f 100644 --- a/tests/falcon_test.py +++ b/tests/falcon_test.py @@ -174,6 +174,48 @@ def on_get(self): assert spec._paths["/hi"]["get"] == expected + def test_path_with_and_without_suffix(self, app, spec_factory): + class HelloResource: + def on_get_hello(self): + """A greeting endpoint. + --- + description: get a greeting + responses: + 200: + description: said hello + """ + return "dummy" + + def on_get(self): + """A valid method. + --- + description: this should pass + responses: + 200: + description: said hi + """ + return "on get" + + on_get_suffix_expected = { + "description": "get a greeting", + "responses": {"200": {"description": "said hello"}}, + } + + on_get_expected = { + "description": "this should pass", + "responses": {"200": {"description": "said hi"}}, + } + + hello_resource_with_suffix = HelloResource() + app.add_route("/hi", hello_resource_with_suffix) + app.add_route("/hi/{hello}", hello_resource_with_suffix, suffix="hello") + spec = spec_factory(app) + spec.path(resource=hello_resource_with_suffix) + spec.path(resource=hello_resource_with_suffix) + + assert spec._paths["/hi/{hello}"]["get"] == on_get_suffix_expected + assert spec._paths["/hi"]["get"] == on_get_expected + def test_resource_without_endpoint(self, app, spec_factory): class HelloResource: def on_get(self, req, resp): From 346f65bf049958e312c6e82cc3b63158db7af1fe Mon Sep 17 00:00:00 2001 From: Eric Benson Date: Thu, 16 Jul 2020 13:56:51 -0500 Subject: [PATCH 2/2] Renamed resource=pet_resource Signed-off-by: Eric Benson --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7d86d48..2fc8f16 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ class PetResource: """ pet = get_random_pet() # returns JSON resp.media = pet - + def on_get_one(self, req, resp, pet): """A cute furry animal endpoint. --- @@ -98,8 +98,8 @@ spec.components.schema('Pet', schema=PetSchema) # pass created resource into `path` for APISpec # should be passed twice so the suffix is registered # path should be called n + 1 times where n is the number of suffixes -spec.path(resource=random_pet_resource) -spec.path(resource=random_pet_resource) +spec.path(resource=pet_resource) +spec.path(resource=pet_resource) ``` ### Generated OpenAPI Spec