Redocly / redocly-vs-code

Redocly VS Code extension

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bug: header fields `name` and `in` not expected here - Parameter Object

jeremyfiel opened this issue · comments

What happened?

I have a large set of headers defined as components. It seems only some of the headers will produce an error for not expected here.
My JSON schema is valid and consistent with all other headers using a similar schema.

Minimal reproducible OpenAPI snippet

{
    "components": {
        "parameters": {
            "header_Content-Language": {
                "name": "Content-Language",
                "in": "header",
                "description": "Specifies the languages for which the entity-body is intended ",
                "required": false,
                "schema": {
                    "type": "string"
                }
            },
            "header_Location": {
                "name": "Location",
                "in": "header",
                "description": "Used to redirect the recipient to a location other the request URI for completion of the request or identification of a new resource",
                "required": false,
                "schema": {
                    "$ref": "../../../common/uriType_v01.json"
                }
            }
        }
    }
}

Screenshots
Example of the error produced
image

Example of a valid component schema
image

Additional Content
Version: 1.65.2 (user setup)
Commit: c722ca6c7eed3d7987c0d5c3df5c45f6b15e77d1
Date: 2022-03-10T14:33:55.248Z
Electron: 13.5.2
Chromium: 91.0.4472.164
Node.js: 14.16.0
V8: 9.1.269.39-electron.0
OS: Windows_NT x64 10.0.19042

Does this show problems if you run openapi lint too?

yes. using beta89 I have the following output

[5] filename-swagger_v1.json:543:5 at #/components/parameters/header_Cache-Control/name

Property name is not expected here.

541 | "header_Cache-Control": {
542 | "description": "Specifies directives that must be obeyed by all caching mechanisms along the request/response chain",
543 | "name": "Cache-Control",
544 | "in": "header",
545 | "schema": {

Error was generated by the spec rule.

[6] filename-swagger_v1.json:544:5 at #/components/parameters/header_Cache-Control/in

Property in is not expected here.

542 | "description": "Specifies directives that must be obeyed by all caching mechanisms along the request/response chain",
543 | "name": "Cache-Control",
544 | "in": "header",
545 | "schema": {
546 | "type": "string"

Error was generated by the spec rule.

[7] filename-swagger_v1.json:535:5 at #/components/parameters/header_ETag/name

Property name is not expected here.

533 | "header_ETag": {
534 | "description": "Defines the entity tag for use with the If-Match and If-None-Match request headers",
535 | "name": "ETag",
536 | "in": "header",
537 | "schema": {

Error was generated by the spec rule.

[8] filename-swagger_v1.json:536:5 at #/components/parameters/header_ETag/in

Property in is not expected here.

534 | "description": "Defines the entity tag for use with the If-Match and If-None-Match request headers",
535 | "name": "ETag",
536 | "in": "header",
537 | "schema": {
538 | "type": "string"

Error was generated by the spec rule.

[9] filename-swagger_v1.json:516:5 at #/components/parameters/header_Last-Modified/name

Property name is not expected here.

514 | },
515 | "header_Last-Modified": {
516 | "name": "Last-Modified",
517 | "in": "header",
518 | "description": "Specifies the date and time the representational state of the resource was last modified",

Error was generated by the spec rule.

[10] filename-swagger_v1.json:517:5 at #/components/parameters/header_Last-Modified/in

Property in is not expected here.

515 | "header_Last-Modified": {
516 | "name": "Last-Modified",
517 | "in": "header",
518 | "description": "Specifies the date and time the representational state of the resource was last modified",
519 | "required": true,

Error was generated by the spec rule.

[11] filename-swagger_v1.json:525:5 at #/components/parameters/header_Content-Language/name

Property name is not expected here.

523 | },
524 | "header_Content-Language": {
525 | "name": "Content-Language",
526 | "in": "header",
527 | "description": "Specifies the languages for which the entity-body is intended ",

Error was generated by the spec rule.

[12] filename-swagger_v1.json:526:5 at #/components/parameters/header_Content-Language/in

Property in is not expected here.

524 | "header_Content-Language": {
525 | "name": "Content-Language",
526 | "in": "header",
527 | "description": "Specifies the languages for which the entity-body is intended ",
528 | "required": false,

Error was generated by the spec rule.

more context on this issue.

OAS 3.x.x supports a header object and the schema does not allow name and in. Otherwise, the schema is semantically identical to the parameter object. The header object is expected in the headers map defined in components>headers and is supposed to be used as Response headers throughout the definition. If I want to send request headers where a requestBody doesn't exist, such as a GET operation using caching headers ETag, If-Match etc.., OAS forces me to use parameters rather than headers components because the headers map is only available in the encoding object under content negotiation of a request body. A GET operation shouldn't have a request body but there are plenty of times I need to use headers in the request.

The bug I reported is mis-identifying parameter objects with the in: header identifier and flagging them as invalid.

{
    "openapi": "3.0.3",
    "servers": [
        {
            "url": "https://__host__/"
        }
    ],
    "info": {
        "description": "Test api",
        "version": "1.0.0",
        "title": "empty title"
    },
    "paths": {
        "/some-path/{aoid}": {
            "get": {
                "description": "some description",
                "tags": [
                    "a tag"
                ],
                "summary": "what it does",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/path_aoid"
                    },
                    {
                        "$ref": "#/components/parameters/query_filter"
                    },
                    {
                        "$ref": "#/components/parameters/header_If-Match"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Ok",
                        "headers": {
                            "ETag": {
                                "$ref": "#/components/headers/ETag"
                            },
                            "Last-Modified": {
                                "$ref": "#/components/headers/Last-Modified"
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "string"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "parameters": {
            "path_aoid": {
                "name": "aoid",
                "in": "path",
                "description": "a param",
                "required": true,
                "schema": {
                    "$ref": "../../../common/associateOIDType_v01.json"
                }
            },
            "header_If-Match": {
                "name": "If-Match",
                "in": "header",
                "description": "If-Match caching policy",
                "required": true,
                "schema": {
                    "type": "string"
                }
            }
        },
        "headers": {
            "ETag": {
                "description": "Defines the entity tag for use with the If-Match and If-None-Match request headers",
                "schema": {
                    "type": "string"
                }
            },
           "Last-Modified": {
				"name": "Last-Modified",
				"in": "header",
				"description": "Specifies the date and time the representational state of the resource was last modified",
				"required": true,
				"schema": {
					"type": "string"
				}
			}
        }
    }
}

If a parameter object is defined in components>parameters and uses in: header as the identifier, it should be treated as a parameter object, not a header object.

I just tried your snippet and I can't see the issue:

image

Make sure you are not referencing headers from parameters and vice versa. This may be the issue.

You can see in the original screenshot where i have everything in parameters and some are having errors and some are not. The snippet was just explaining the issue. In my actual file I'm not using components>headers at all. Only components>parameters

In your original file do you reference components>parameters from the headers section?

i am referencing components>parameters from response object > headers map.

That's the problem.

Object->headers expects header objects, not parameter objects so openapi-cli correctly reports the name and in fields are not expected there.

I understand your intent and that you do it on purpose but it may mean someone made a mistake and we don't have a way to distinguish.
You have to add some duplication to be spec compliant.

Moreover, this goes against OAS 3 spec. Official OAS 3 json-schema cannot catch this issue though due to json-schema limitations.

If you check the corresponding schema you will notice oneOf that allows either header object or ref object. You have a ref there so the validation pass but json-schema can't validate the destination of $ref. You can check it by pointing $ref to something completely unrelated, e.g. Response Object and your definition will still pass official JSON Schema validation.

If you check the definition of Header object it has additionalProperties: false so it does not allow any other properties.
If you dereference your schema and run it against OAS 3 official JSON Schema it will show you that name and in fields are not expected there.

thanks @RomanHotsiy

I think you're right. I'm OOO but will definitely check that when I return. It makes more sense now why only some headers are showing name and in errors.. they are probably referenced in the Response>Headers object.

@RomanHotsiy
I'm looking at this again and I'm having difficulty convincing myself and others that it's actually a problem.
Swagger, stoplight and redoc are all rendering these headers in the UI as expected, but the linting is failing. Changing all of our specs to clear this error is going to be a tough sell to our design team.

is this behavior correct or does it assume the header pointer and ignore the name/in props?

invalid example
https://redocly.github.io/redoc/?url=https://gist.githubusercontent.com/jeremyfiel/85fd4f39944a472cb6bb60d529e2b602/raw/bef1af5735073d897ef99aaee58382b06f29a67c/openapi-test-headers.json#tag/a-tag

valid example
https://redocly.github.io/redoc/?url=https://gist.githubusercontent.com/jeremyfiel/0ec5f45029e701f66d4c62521d41f24c/raw/363c4c47e0d5bc03742ceca95ab1e0672b0eba4f/openapi-test-headers-in-components.json#tag/a-tag/paths/~1some-path~1{aoid}/get

both have the same output on the UI.

The purpose of Redoc is not to enforce the spec. It is extremely lenient and you may make a lot of errors. Its goal is to render what it can as best as it can. The goal of the lint command here is something else entirely. It's to enforce API design standards including specification compliance.

From the spec:

  1. name MUST NOT be specified, it is given in the corresponding headers map.
  2. in MUST NOT be specified, it is implicitly in header.
  3. All traits that are affected by the location MUST be applicable to a location of header (for example, style).

Source OAS 3.1: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#header-object
Source OAS 3.0: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#header-object