redocly-cli
redocly-cli copied to clipboard
Reported Error location does not point at correct node for Parameters
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:
- Given this
redocly.yaml
file
extends:
- recommended
- 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"
}
}
}
}
}
}
}
-
Run this command with these arguments...
redocly lint --config redocly.yaml fcc-classroom-userdata-openapi.json
-
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)
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.
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.
@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.
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.
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.