diff --git a/foca/api/register_openapi.py b/foca/api/register_openapi.py index 8881005..320f76e 100644 --- a/foca/api/register_openapi.py +++ b/foca/api/register_openapi.py @@ -12,6 +12,12 @@ # Get logger instance logger = logging.getLogger(__name__) +# Path Item object fields which contain an Operation object (ie: HTTP verbs). +# Reference: https://swagger.io/specification/v3/#path-item-object +_OPERATION_OBJECT_FIELDS = frozenset({ + "get", "put", "post", "delete", "options", "head", "patch", "trace", +}) + def register_openapi( app: App, @@ -51,7 +57,9 @@ def register_openapi( if spec.add_operation_fields is not None: for key, val in spec.add_operation_fields.items(): for path_item_object in spec_parsed.get('paths', {}).values(): - for operation_object in path_item_object.values(): + for operation, operation_object in path_item_object.items(): + if operation not in _OPERATION_OBJECT_FIELDS: + continue operation_object[key] = val logger.debug( f"Added operation fields: {spec.add_operation_fields}" diff --git a/tests/api/test_register_openapi.py b/tests/api/test_register_openapi.py index 546f425..cc8a7d4 100644 --- a/tests/api/test_register_openapi.py +++ b/tests/api/test_register_openapi.py @@ -21,6 +21,8 @@ PATH_SPECS_2_YAML_ADDITION = DIR / "openapi_2_petstore.addition.yaml" PATH_SPECS_3_YAML_ORIGINAL = DIR / "openapi_3_petstore.original.yaml" PATH_SPECS_3_YAML_MODIFIED = DIR / "openapi_3_petstore.modified.yaml" +PATH_SPECS_3_PATHITEMPARAM_YAML_ORIGINAL = DIR / "openapi_3_petstore_pathitemparam.original.yaml" +PATH_SPECS_3_PATHITEMPARAM_YAML_MODIFIED = DIR / "openapi_3_petstore_pathitemparam.modified.yaml" PATH_SPECS_INVALID_JSON = DIR / "invalid.json" PATH_SPECS_INVALID_YAML = DIR / "invalid.openapi.yaml" PATH_NOT_FOUND = DIR / "does/not/exist.yaml" @@ -64,6 +66,15 @@ "disable_auth": False, "connexion": CONNEXION_CONFIG, } +SPEC_CONFIG_3_PATHITEMPARAM = { + "path": PATH_SPECS_3_PATHITEMPARAM_YAML_ORIGINAL, + "path_out": PATH_SPECS_3_PATHITEMPARAM_YAML_MODIFIED, + "append": [APPEND], + "add_operation_fields": OPERATION_FIELDS_3, + "add_security_fields": SECURITY_FIELDS_3, + "disable_auth": False, + "connexion": CONNEXION_CONFIG, +} SPEC_CONFIG_2_JSON = deepcopy(SPEC_CONFIG_2) SPEC_CONFIG_2_JSON['path'] = PATH_SPECS_2_JSON_ORIGINAL SPEC_CONFIG_2_LIST = deepcopy(SPEC_CONFIG_2) @@ -92,6 +103,16 @@ def test_openapi_3_yaml(self): res = register_openapi(app=app, specs=spec_configs) assert isinstance(res, App) + def test_openapi_3_pathitemparam_yaml(self): + """ + Successfully register OpenAPI 3 YAML specs with PathItem.parameters + field with Connexion app. + """ + app = App(__name__) + spec_configs = [SpecConfig(**SPEC_CONFIG_3_PATHITEMPARAM)] + res = register_openapi(app=app, specs=spec_configs) + assert isinstance(res, App) + def test_openapi_2_json(self): """Successfully register OpenAPI 2 JSON specs with Connexion app.""" app = App(__name__) diff --git a/tests/test_files/openapi_3_petstore_pathitemparam.modified.yaml b/tests/test_files/openapi_3_petstore_pathitemparam.modified.yaml new file mode 100644 index 0000000..41f1afa --- /dev/null +++ b/tests/test_files/openapi_3_petstore_pathitemparam.modified.yaml @@ -0,0 +1,114 @@ +components: + schemas: + Error: + properties: + code: + format: int32 + type: integer + message: + type: string + required: + - code + - message + type: object + Pet: + properties: + id: + format: int64 + type: integer + name: + type: string + tag: + type: string + required: + - id + - name + type: object + Pets: + items: + $ref: '#/components/schemas/Pet' + type: array +info: + license: + name: MIT + title: Swagger Petstore + version: 1.0.0 +openapi: 3.0.0 +paths: + /pets: + get: + operationId: listPets + parameters: + - description: How many items to return at one time (max 100) + in: query + name: limit + required: false + schema: + format: int32 + type: integer + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Pets' + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + default: + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + description: unexpected error + summary: List all pets + tags: + - pets + x-openapi-router-controller: controllers + post: + operationId: createPets + responses: + '201': + description: Null response + default: + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + description: unexpected error + summary: Create a pet + tags: + - pets + x-openapi-router-controller: controllers + /pets/{petId}: + parameters: + - description: The id of the pet to retrieve + in: path + name: petId + required: true + schema: + type: string + get: + operationId: showPetById + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: Expected response to a valid request + default: + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + description: unexpected error + summary: Info for a specific pet + tags: + - pets + x-openapi-router-controller: controllers +servers: +- url: http://petstore.swagger.io/v2 diff --git a/tests/test_files/openapi_3_petstore_pathitemparam.original.yaml b/tests/test_files/openapi_3_petstore_pathitemparam.original.yaml new file mode 100644 index 0000000..f301de5 --- /dev/null +++ b/tests/test_files/openapi_3_petstore_pathitemparam.original.yaml @@ -0,0 +1,122 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v2 +security: + - bearerAuth: [] +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + security: + - bearerAuth: [] + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + security: + - bearerAuth: [] + /pets/{petId}: + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string