diff --git a/applications/__tests__/__snapshots__/e2e-lint.test.js.snap b/applications/__tests__/__snapshots__/e2e-lint.test.js.snap index a2fe0d8..93e7e7c 100644 --- a/applications/__tests__/__snapshots__/e2e-lint.test.js.snap +++ b/applications/__tests__/__snapshots__/e2e-lint.test.js.snap @@ -226,9 +226,6 @@ ERROR! Expected an array but got: { foo: 'boolean', bar: 'number' } ERROR! Cannot merge empty lists. ERROR! Cannot merge 'array' types. -WARNING! Got an 'undefined' type. -WARNING! Got an 'undefined' type. -WARNING! Got an 'undefined' type. [1] applications/resources/openapi-and.yaml:16:17 at #/paths/~1test/get/responses/200/content/application~1json/example Example value must conform to the schema: must have required property 'bar'. @@ -657,9 +654,31 @@ run \`redocly lint --generate-ignore-file\` to add all problems to the ignore fi " `; +exports[`lint > openapi with redundant key in $descriptions 1`] = ` +"validating applications/resources/openapi-with-redundant-description.yaml... +[1] applications/resources/openapi-with-redundant-description.yaml:12:9 at #/components/x-types/Item/$descriptions/wrong + +Key "wrong" does not exist in the object type + +10 | $descriptions: +11 | id: User identifier. +12 | wrong: Should be reported! + | ^^^^^ +13 | + +Warning was generated by the x-types/no-undefined-descriptions rule. + + +applications/resources/openapi-with-redundant-description.yaml: validated in ms + +Woohoo! Your API description is valid. 🎉 +You have 1 warning. + +" +`; + exports[`lint > openapi with writeOnly and readOnly fields 1`] = ` "validating applications/resources/openapi-with-writeonly-and-readonly.yaml... -WARNING! Got an 'undefined' type. [1] applications/resources/openapi-with-writeonly-and-readonly.yaml:21:23 at #/paths/~1test/get/requestBody/content/application~1json/examples/Incorrect/value/id Example value must conform to the schema: \`id\` property must NOT be valid. diff --git a/applications/__tests__/e2e-lint.test.js b/applications/__tests__/e2e-lint.test.js index a534997..6ccb8a9 100644 --- a/applications/__tests__/e2e-lint.test.js +++ b/applications/__tests__/e2e-lint.test.js @@ -88,4 +88,11 @@ describe('lint', () => { ) expect(stripCWD(stderr)).toMatchSnapshot() }) + + test('openapi with redundant key in $descriptions', () => { + const {stdout: stderr} = runCommand( + 'redocly lint applications/resources/openapi-with-redundant-description.yaml --config=applications/x-redocly.yaml 2>&1' + ) + expect(stripCWD(stderr)).toMatchSnapshot() + }) }) diff --git a/applications/resources/openapi-with-redundant-description.yaml b/applications/resources/openapi-with-redundant-description.yaml new file mode 100644 index 0000000..9bf6924 --- /dev/null +++ b/applications/resources/openapi-with-redundant-description.yaml @@ -0,0 +1,12 @@ +openapi: 3.1.0 +info: + title: Test + version: 1.0.0 +components: + x-types: + Item: + id: + $readonly: string + $descriptions: + id: User identifier. + wrong: Should be reported! diff --git a/applications/x-types-adapter.js b/applications/x-types-adapter.js index 6a9ecaa..f43b1ae 100644 --- a/applications/x-types-adapter.js +++ b/applications/x-types-adapter.js @@ -108,7 +108,6 @@ export const translateXTypeToSchema = xType => { } if (xType === 'undefined') { - console.warn("WARNING! Got an 'undefined' type.") return {not: {}} } diff --git a/applications/x-types-plugin.js b/applications/x-types-plugin.js index b0e6542..30d0d43 100644 --- a/applications/x-types-plugin.js +++ b/applications/x-types-plugin.js @@ -4,7 +4,7 @@ import { generateNamedXTypes, createRefs, } from './x-types-decorators.js' -import {noRefNeighbors} from './x-types-rules.js' +import {noRefNeighbors, noUndefinedDescriptions} from './x-types-rules.js' import {isObject} from './x-types-utils.js' const getType = value => { @@ -112,6 +112,7 @@ export default () => ({ rules: { oas3: { 'no-$ref-neighbors': noRefNeighbors, + 'no-undefined-descriptions': noUndefinedDescriptions, }, }, @@ -119,6 +120,7 @@ export default () => ({ all: { rules: { 'x-types/no-$ref-neighbors': 'error', + 'x-types/no-undefined-descriptions': 'warn', }, }, }, diff --git a/applications/x-types-rules.js b/applications/x-types-rules.js index d60aca9..43729f1 100644 --- a/applications/x-types-rules.js +++ b/applications/x-types-rules.js @@ -1,3 +1,5 @@ +import {isNotEmptyObject} from '@redocly/openapi-core/lib/utils.js' + export const noRefNeighbors = () => { return { ref: { @@ -13,3 +15,22 @@ export const noRefNeighbors = () => { }, } } + +export const noUndefinedDescriptions = () => { + return { + XTypeObject: { + leave: (obj, ctx) => { + if (isNotEmptyObject(obj.$descriptions)) { + for (const key in obj.$descriptions) { + if (obj[key] === undefined) { + ctx.report({ + message: `Key "${key}" does not exist in the object type`, + location: ctx.location.child(['$descriptions', key]).key(), + }) + } + } + } + }, + }, + } +} diff --git a/extensions.md b/extensions.md index 9eac96e..03e3f65 100644 --- a/extensions.md +++ b/extensions.md @@ -7,8 +7,8 @@ It is possible to add additional context to types and values using other reserve | Keyword | Description | Usage | | ------------------------------------------------- | ------------------------------------------------------------------ | ----- | | $descriptions [🔗](#descriptions) | An object containing descriptions of the fields at the same level. | key | -| $writeonly [🔗](#read-only-and-write-only-fields) | Describes a write-only field. | key | -| $readonly [🔗](#read-only-and-write-only-fields) | Describes a read-only field. | key | +| $writeonly [🔗](#read-only-and-write-only-fields) | Describes a field that can only appear in requests. | key | +| $readonly [🔗](#read-only-and-write-only-fields) | Describes a field that can only appear in responses. | key | | $discriminator [🔗](#discriminator) | Represents an OpenAPI discriminator. | key | Those keywords can be helpful for describing OpenAPI-compatible types. @@ -29,7 +29,7 @@ They can only be used in objects: Descriptions are propagated to the OpenAPI schema as the `description` fields of the corresponding properties. -### Read-only and Write-only fields +### Read-only and Write-only Fields The `$writeonly` and `$readonly` fields contain properties that should be present only in requests or responses respectively. Consider this example: