Skip to content

Commit

Permalink
feat: add new 'ibm-no-ref-in-example' rule (#669)
Browse files Browse the repository at this point in the history
This commit introduces the new 'ibm-no-ref-in-example'
rule which will check to make sure that "$ref" is not
used in "example" fields.  Note that $ref is valid in an
"examples" field, but not in an "example" field.

Signed-off-by: Phil Adams <[email protected]>
  • Loading branch information
padamstx authored Jun 20, 2024
1 parent 45c88a0 commit bcf8b76
Show file tree
Hide file tree
Showing 7 changed files with 462 additions and 0 deletions.
68 changes: 68 additions & 0 deletions docs/ibm-cloud-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ which is delivered in the `@ibm-cloud/openapi-ruleset` NPM package.
* [ibm-no-nullable-properties](#ibm-no-nullable-properties)
* [ibm-no-operation-requestbody](#ibm-no-operation-requestbody)
* [ibm-no-optional-properties-in-required-body](#ibm-no-optional-properties-in-required-body)
* [ibm-no-ref-in-example](#ibm-no-ref-in-example)
* [ibm-no-space-in-example-name](#ibm-no-space-in-example-name)
* [ibm-no-superfluous-allof](#ibm-no-superfluous-allof)
* [ibm-no-unsupported-keywords](#ibm-no-unsupported-keywords)
* [ibm-openapi-tags-used](#ibm-openapi-tags-used)
* [ibm-operation-responses](#ibm-operation-responses)
Expand Down Expand Up @@ -351,6 +353,12 @@ should probably be required instead of optional.</td>
<td>oas3</td>
</tr>
<tr>
<td><a href="#ibm-no-ref-in-example">ibm-no-ref-in-example</a></td>
<td>info</td>
<td>Makes sure that <code>$ref</code> is not used within an <code>example</code> field.</td>
<td>oas3</td>
</tr>
<tr>
<td><a href="#ibm-no-space-in-example-name">ibm-no-space-in-example-name</a></td>
<td>warn</td>
<td>The name of an entry in an <code>examples</code> field should not contain a space.</td>
Expand Down Expand Up @@ -3533,6 +3541,66 @@ paths:
</table>


### ibm-no-ref-in-example
<table>
<tr>
<td><b>Rule id:</b></td>
<td><b>ibm-no-ref-in-example</b></td>
</tr>
<tr>
<td valign=top><b>Description:</b></td>
<td>Within an OpenAPI document, the <code>$ref</code> field may appear within an <code>examples</code> field, but it is not valid within an <code>example</code> field. This rule checks to make sure that <code>$ref</code> is not used within an <code>example</code> field.</td>
</tr>
<tr>
<td><b>Severity:</b></td>
<td>error</td>
</tr>
<tr>
<td><b>OAS Versions:</b></td>
<td>oas3</td>
</tr>
<tr>
<td valign=top><b>Non-compliant example:<b></td>
<td>
<pre>
paths:
/v1/things:
post:
operationId: create_thing
description: Create a new Thing instance.
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Thing'
example:
$ref: '#/components/examples/ThingExample'
</pre>
</td>
</tr>
<tr>
<td valign=top><b>Compliant example:</b></td>
<td>
<pre>
paths:
/v1/things:
post:
operationId: create_thing
description: Create a new Thing instance.
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Thing'
example:
name: 'Thing1'
description: 'An example Thing object'
</pre>
</td>
</tr>
</table>


### ibm-no-space-in-example-name
<table>
<tr>
Expand Down
1 change: 1 addition & 0 deletions packages/ruleset/src/functions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module.exports = {
noAmbiguousPaths: require('./no-ambiguous-paths'),
noNullableProperties: require('./no-nullable-properties'),
noOperationRequestBody: require('./no-operation-requestbody'),
noRefInExample: require('./no-ref-in-example'),
noSuperfluousAllOf: require('./no-superfluous-allof'),
noUnsupportedKeywords: require('./no-unsupported-keywords'),
operationIdCasingConvention: require('./operationid-casing-convention'),
Expand Down
54 changes: 54 additions & 0 deletions packages/ruleset/src/functions/no-ref-in-example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Copyright 2017 - 2024 IBM Corporation.
* SPDX-License-Identifier: Apache2.0
*/

const { isObject } = require('@ibm-cloud/openapi-ruleset-utilities');
const { LoggerFactory } = require('../utils');

let ruleId;
let logger;

module.exports = function (exampleObj, options, context) {
if (!logger) {
ruleId = context.rule.name;
logger = LoggerFactory.getInstance().getLogger(ruleId);
}
return noRefInExample(exampleObj, context.path);
};

/**
* This function will perform a recursive check to make sure that the specified
* "example" value does not contain a "$ref" field.
* @param {*} example the example value to check
* @param {*} path the location of 'example' within the OpenAPI document
* @returns an array containing zero or more error objects
*/
function noRefInExample(example, path) {
logger.debug(`${ruleId}: checking example located at: ${path.join('.')}`);

// If it's not an object, then bail out now since a $ref property is not possible.
if (!isObject(example)) {
return [];
}

const errors = [];

// Check "example" for a $ref property.
if ('$ref' in example) {
logger.debug(
`${ruleId}: found a $ref property at location: ${path.join('.')}.$ref`
);
errors.push({
message: '',
path: [...path, '$ref'],
});
}

// Check each property of "example" recursively for an object containing a $ref property.
for (const p in example) {
errors.push(...noRefInExample(example[p], [...path, p]));
}

return errors;
}
1 change: 1 addition & 0 deletions packages/ruleset/src/ibm-oas.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ module.exports = {
'ibm-no-nullable-properties': ibmRules.noNullableProperties,
'ibm-no-operation-requestbody': ibmRules.noOperationRequestBody,
'ibm-no-optional-properties-in-required-body': ibmRules.optionalRequestBody,
'ibm-no-ref-in-example': ibmRules.noRefInExample,
'ibm-no-space-in-example-name': ibmRules.examplesNameContainsSpace,
'ibm-no-superfluous-allof': ibmRules.noSuperfluousAllOf,
'ibm-no-unsupported-keywords': ibmRules.noUnsupportedKeywords,
Expand Down
1 change: 1 addition & 0 deletions packages/ruleset/src/rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ module.exports = {
noAmbiguousPaths: require('./no-ambiguous-paths'),
noNullableProperties: require('./no-nullable-properties'),
noOperationRequestBody: require('./no-operation-requestbody'),
noRefInExample: require('./no-ref-in-example'),
noSuperfluousAllOf: require('./no-superfluous-allof'),
noUnsupportedKeywords: require('./no-unsupported-keywords'),
operationIdCasingConvention: require('./operationid-casing-convention'),
Expand Down
18 changes: 18 additions & 0 deletions packages/ruleset/src/rules/no-ref-in-example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Copyright 2017 - 2024 IBM Corporation.
* SPDX-License-Identifier: Apache2.0
*/

const { oas3 } = require('@stoplight/spectral-formats');
const { noRefInExample } = require('../functions');
module.exports = {
description: 'The use of $ref is not valid within an example field',
message: '{{description}}',
given: ['$..example'],
severity: 'error',
formats: [oas3],
resolved: false,
then: {
function: noRefInExample,
},
};
Loading

0 comments on commit bcf8b76

Please sign in to comment.