Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reported Error location does not point at correct node for Parameters #1241

Open
jeremyfiel opened this issue Aug 22, 2023 · 5 comments
Open

Comments

@jeremyfiel
Copy link
Contributor

Describe the bug

When an error is produced for a required parameter in the uri, the error location points at the component schema rather than the actual error location in the parameters collection, this makes it difficult to trace which line has the actual error. In this example, the $ref on line 89 should be indicated.

To Reproduce
Steps to reproduce the behavior:

  1. Given this redocly.yaml file
extends:
  - recommended
  1. And this OpenAPI file(s)
{
    "openapi": "3.1.0",
    "info": {
        "title": "freeCodeCamp Classroom",
        "version": "0.0.1",
        "contact": {
            "name": "Classroom Team",
            "email": ""
        },
        "license": {
            "name": "",
            "url": ""
        }
    },
    "servers": [
        {
            "url": "https://api.freecodecamp.org"
        }
    ],
    "security": [],
    "tags": [
        {
            "name": "Classroom user data",
            "description": ""
        }
    ],
    "paths": {
        "/classroom/userData": {
            "get": {
                "summary": "",
                "description": "",
                "operationId": "getUserData",
                "tags": [
                    "Classroom user data"
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/header_accept"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/userData"
                                }
                            }
                        }
                    },
                    "400": {
                        "$ref": "#/components/responses/400"
                    },
                    "401": {
                        "$ref": "#/components/responses/401"
                    },
                    "403": {
                        "$ref": "#/components/responses/403"
                    },
                    "404": {
                        "$ref": "#/components/responses/404"
                    },
                    "500": {
                        "$ref": "#/components/responses/500"
                    },
                    "503": {
                        "$ref": "#/components/responses/503"
                    }
                }
            },
            "post": {
                "summary": "",
                "description": "",
                "operationId": "queryUserData",
                "tags": [
                    "Classroom user data"
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/header_contentType"
                    },
                    {
                        "$ref": "#/components/parameters/path_userId"
                    }
                ],
                "requestBody": {
                    "$ref": "#/components/requestBodies/userDataRequest"
                },
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/userData"
                                }
                            }
                        }
                    },
                    "400": {
                        "$ref": "#/components/responses/400"
                    },
                    "401": {
                        "$ref": "#/components/responses/401"
                    },
                    "403": {
                        "$ref": "#/components/responses/403"
                    },
                    "404": {
                        "$ref": "#/components/responses/404"
                    },
                    "500": {
                        "$ref": "#/components/responses/500"
                    },
                    "503": {
                        "$ref": "#/components/responses/503"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "problem_json_error": {
                "type": "object",
                "properties": {
                    "type": {
                        "type": "string"
                    },
                    "status": {
                        "type": "number",
                        "minimum": 200,
                        "maximum": 511
                    },
                    "title": {
                        "type": "string"
                    },
                    "detail": {
                        "type": "string"
                    },
                    "instance": {
                        "type": "string",
                        "format": "uri-reference"
                    }
                }
            },
            "userData": {
                "$id": "https://api.freecodecamp.org/classroom/userData",
                "$schema": "https://json-schema.org/draft/2020-12/schema",
                "description": "list of students",
                "type": "array",
                "uniqueItems": true,
                "items": {
                    "type": "object",
                    "properties": {
                        "user": {
                            "type": "object",
                            "properties": {
                                "id": {
                                    "type": "string"
                                },
                                "email": {
                                    "description": "student identifier",
                                    "type": "string",
                                    "format": "email"
                                }
                            }
                        },
                        "certifications": {
                            "description": "name of the certification (\"2022/responsive-web-design\")",
                            "type": "array",
                            "uniqueItems": true,
                            "minItems": 1,
                            "items": {
                                "description": "name of the certification block",
                                "type": "object",
                                "propertyNames": {
                                    "pattern": "^[A-Za-z0-9][\/A-Za-z0-9-]*[A-Za-z]$"
                                },
                                "additionalProperties": {
                                    "type": "object",
                                    "unevaluatedProperties": false,
                                    "properties": {
                                        "blocks": {
                                            "description": "List of the challenges/blocks inside of a certification",
                                            "type": "array",
                                            "uniqueItems": true,
                                            "items": {
                                                "type": "object",
                                                "minProperties": 1,
                                                "additionalProperties": {
                                                    "description": "name of the block (learn-css-...)",
                                                    "type": "object",
                                                    "properties": {
                                                        "completedChallenges": {
                                                            "type": "array",
                                                            "description": "list of challenges completed by the student inside of this block",
                                                            "uniqueItems": true,
                                                            "items": {
                                                                "$ref": "#/$defs/Challenge"
                                                            }
                                                        }
                                                    },
                                                    "required": [
                                                        "completedChallenges"
                                                    ]
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "required": [
                        "user",
                        "certifications"
                    ]
                },
                "$defs": {
                    "Challenge": {
                        "description": "A representation of an individual challenge",
                        "type": "object",
                        "unevaluatedProperties": false,
                        "required": [
                            "id",
                            "challengeName",
                            "completedDate"
                        ],
                        "properties": {
                            "id": {
                                "description": "A unique identifier of a completed course",
                                "type": "string",
                                "minLength": 1
                            },
                            "challengeName": {
                                "type": "string"
                            },
                            "completedDate": {
                                "description": "A Unix timestamp representation",
                                "type": "integer"
                            },
                            "files": {
                                "description": "A list of files pertaining to the challenge",
                                "type": "array",
                                "uniqueItems": true,
                                "items": {
                                    "$ref": "#/$defs/File"
                                }
                            }
                        }
                    },
                    "File": {
                        "description": "A representation of a file",
                        "type": "string",
                        "format": "binary"
                    }
                }
            }
        },
        "parameters": {
            "header_accept": {
                "name": "accept",
                "description": "",
                "in": "header",
                "schema": {
                    "type": "string"
                },
                "examples": {
                    "accept_header": {
                        "summary": "A standard header indicating the accepted media type",
                        "value": "application/json"
                    }
                }
            },
            "header_contentType": {
                "name": "content-type",
                "description": "",
                "in": "header",
                "schema": {
                    "type": "string"
                },
                "examples": {
                    "content-type": {
                        "summary": "A standard header indicating the content media type",
                        "value": "application/json"
                    }
                }
            },
            "path_userId": {
                "name": "user-id",
                "description": "",
                "in": "path",
                "schema": {
                    "type": "string"
                },
                "required": true
            }
        },
        "requestBodies": {
            "userDataRequest": {
                "description": "",
                "content": {
                    "application/json": {
                        "schema": {
                            "$schema": "https://json-schema.org/draft/2020-12/schema",
                            "title": "UserDataRequest",
                            "description": "Queries a collection of users and certifications",
                            "type": "object",
                            "unevaluatedProperties": false,
                            "properties": {
                                "users": {
                                    "type": "array",
                                    "uniqueItems": true,
                                    "minItems": 1,
                                    "prefixItems": [
                                        {
                                            "type": "object",
                                            "unevaluatedProperties": false,
                                            "properties": {
                                                "id": {
                                                    "type": "string"
                                                }
                                            },
                                            "required": [
                                                "id"
                                            ]
                                        }
                                    ],
                                    "items": {
                                        "description": "name of the certification block",
                                        "type": "string",
                                        "pattern": "^[A-Za-z0-9][\/A-Za-z0-9-]*[A-Za-z]$"
                                    }
                                }
                            },
                            "required": [
                                "users"
                            ]
                        }
                    }
                },
                "required": true
            }
        },
        "responses": {
            "400": {
                "description": "Bad Request",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/problem_json_error"
                        }
                    }
                }
            },
            "401": {
                "description": "Unauthorized",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/problem_json_error"
                        }
                    }
                }
            },
            "403": {
                "description": "Forbidden",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/problem_json_error"
                        }
                    }
                }
            },
            "404": {
                "description": "Not Found",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/problem_json_error"
                        }
                    }
                }
            },
            "500": {
                "description": "Internal Server Error",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/problem_json_error"
                        }
                    }
                }
            },
            "503": {
                "description": "Service Unavailable",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/problem_json_error"
                        }
                    }
                }
            }
        }
    }
}
  1. Run this command with these arguments...
    redocly lint --config redocly.yaml fcc-classroom-userdata-openapi.json

  2. See error

  302:25  error    path-parameters-defined  Path parameter `user-id` is not used in the path `/classroom/userData`.

Expected behavior

The error location line number shall be the correct node where the error occurs, not the referenced location

Redocly Version(s)

redocly --version
1.0.2

Node.js Version(s)

node v14.18.1 (npm v6.14.15)

@jeremyfiel jeremyfiel added the Type: Bug Something isn't working label Aug 22, 2023
@lornajane
Copy link
Collaborator

Thanks for raising this, but the current behaviour makes sense to me. The problem that the linter describes is that there's a mismatch with the parameter name or where it should be found (path, header, etc). The parameter object itself is therefore the location of the problem, it's a $ref so we point to its declaration, which in this example is in components. The message then provides the context of which path the parameter was expected in - the path and the parameter declaration are the only places in play, the $ref syntax is sort of transparent.

Reporting in the location that makes the error apparent is also consistent with how the other types of error are reported. I'm open to more discussion on this, but for the specific scenario given here, I am not in favour of making changes.

@RomanHotsiy
Copy link
Member

We used to show "referenced from" for every error if it's referenced. This was gone after some major refactoring but I believe we can bring it back.

I think this would be a great middle-ground.

@jeremyfiel
Copy link
Contributor Author

@lornajane I'm not sure I agree with your comment

The parameter object itself is therefore the location of the problem...

The problem lies in the location where a reusable component has been referenced, the problem itself is not the parameter definition, but the location of its use.

@RomanHotsiy sounds reasonable.

@jeremyfiel
Copy link
Contributor Author

some additional context on why I raised this,

using the VS Code extension in the design phase, in the problems tab, you can click the error and it will navigate to that line number. the issue I raised is with the current implementation; the error should point you to the location of the error, not the referenced location (components), to quickly fix the problem.

The behavior on the cli may be acceptable with the context provided because you can't interact with the output in the same way as the vsc extension. This is may be considered a separate use case.

@SmoliyY SmoliyY added p3 Type: Enhancement and removed Type: Bug Something isn't working labels Aug 23, 2023
@SmoliyY
Copy link
Contributor

SmoliyY commented Aug 23, 2023

Thank you @jeremyfiel! In such case this is not a bug, but rather enhancement.

We used to show "referenced from" for every error if it's referenced. This was gone after some major refactoring but I believe we can bring it back. I think this would be a great middle-ground.

For me @RomanHotsiy suggestion sounds reasonable. We can use proposed solution in this case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants