-
Notifications
You must be signed in to change notification settings - Fork 14
CLOUDP-287249: IPA-123: Validate enums must be UPPER_SNAKE_CASE #332
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
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
48b0633
CLOUDP-287247: IPA-109: Validate custom method must use colon (:) fol…
yelizhenden-mdb a8ec29f
prettier fix
yelizhenden-mdb 9a7fc40
fix
yelizhenden-mdb 49de978
CLOUDP-287249: IPA-123: Validate enums must be UPPER_SNAKE_CASE
yelizhenden-mdb 0dc79a5
CLOUDP-287249: IPA-123: Validate enums must be UPPER_SNAKE_CASE
yelizhenden-mdb d47a778
Merge branch 'main' into CLOUDP-287249
yelizhenden-mdb fb874f5
CLOUDP-287249: IPA-123: Validate enums must be UPPER_SNAKE_CASE
yelizhenden-mdb a5d137e
prettier fix
yelizhenden-mdb 35c3f29
enum index fix
yelizhenden-mdb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
151 changes: 151 additions & 0 deletions
151
tools/spectral/ipa/__tests__/eachEnumValueMustBeUpperSnakeCase.test.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import testRule from './__helpers__/testRule'; | ||
import { DiagnosticSeverity } from '@stoplight/types'; | ||
|
||
testRule('xgen-IPA-123-enum-values-must-be-upper-snake-case', [ | ||
{ | ||
name: 'valid schema - components.schemas', | ||
document: { | ||
components: { | ||
schemas: { | ||
SchemaName: { | ||
properties: { | ||
exampleProperty: { | ||
enum: ['EXAMPLE_A', 'EXAMPLE_B'], | ||
type: 'string', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
errors: [], | ||
}, | ||
{ | ||
name: 'invalid schema with exception - components.schemas', | ||
document: { | ||
components: { | ||
schemas: { | ||
SchemaName: { | ||
'x-xgen-IPA-exception': { | ||
'xgen-IPA-123-enum-values-must-be-upper-snake-case': 'reason', | ||
}, | ||
properties: { | ||
exampleProperty: { | ||
enum: ['exampleA', 'exampleB'], | ||
type: 'string', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
errors: [], | ||
}, | ||
{ | ||
name: 'invalid schema - components.schemas', | ||
document: { | ||
components: { | ||
schemas: { | ||
SchemaName: { | ||
properties: { | ||
exampleProperty: { | ||
enum: ['exampleA', 'exampleB'], | ||
type: 'string', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
errors: [ | ||
{ | ||
code: 'xgen-IPA-123-enum-values-must-be-upper-snake-case', | ||
message: 'exampleA enum value must be UPPER_SNAKE_CASE. http://go/ipa/123', | ||
path: ['components', 'schemas', 'SchemaName', 'properties', 'exampleProperty', 'enum', '0'], | ||
severity: DiagnosticSeverity.Warning, | ||
}, | ||
{ | ||
code: 'xgen-IPA-123-enum-values-must-be-upper-snake-case', | ||
message: 'exampleB enum value must be UPPER_SNAKE_CASE. http://go/ipa/123', | ||
path: ['components', 'schemas', 'SchemaName', 'properties', 'exampleProperty', 'enum', '1'], | ||
severity: DiagnosticSeverity.Warning, | ||
}, | ||
], | ||
}, | ||
{ | ||
name: 'valid schema - paths.*', | ||
document: { | ||
paths: { | ||
'/a/{exampleId}': { | ||
get: { | ||
parameters: [ | ||
{ | ||
schema: { | ||
type: 'string', | ||
enum: ['EXAMPLE_A', 'EXAMPLE_B'], | ||
}, | ||
}, | ||
], | ||
}, | ||
}, | ||
}, | ||
}, | ||
errors: [], | ||
}, | ||
{ | ||
name: 'invalid schema with exception - paths.*', | ||
document: { | ||
paths: { | ||
'/a/{exampleId}': { | ||
get: { | ||
parameters: [ | ||
{ | ||
schema: { | ||
'x-xgen-IPA-exception': { | ||
'xgen-IPA-123-enum-values-must-be-upper-snake-case': 'reason', | ||
}, | ||
type: 'string', | ||
enum: ['exampleA', 'exampleB'], | ||
}, | ||
}, | ||
], | ||
}, | ||
}, | ||
}, | ||
}, | ||
errors: [], | ||
}, | ||
{ | ||
name: 'invalid schema - paths.*', | ||
document: { | ||
paths: { | ||
'/a/{exampleId}': { | ||
get: { | ||
parameters: [ | ||
{ | ||
schema: { | ||
type: 'string', | ||
enum: ['exampleA', 'exampleB'], | ||
}, | ||
}, | ||
], | ||
}, | ||
}, | ||
}, | ||
}, | ||
errors: [ | ||
{ | ||
code: 'xgen-IPA-123-enum-values-must-be-upper-snake-case', | ||
message: 'exampleA enum value must be UPPER_SNAKE_CASE. http://go/ipa/123', | ||
path: ['paths', '/a/{exampleId}', 'get', 'parameters', '0', 'schema', 'enum', '0'], | ||
severity: DiagnosticSeverity.Warning, | ||
}, | ||
{ | ||
code: 'xgen-IPA-123-enum-values-must-be-upper-snake-case', | ||
message: 'exampleB enum value must be UPPER_SNAKE_CASE. http://go/ipa/123', | ||
path: ['paths', '/a/{exampleId}', 'get', 'parameters', '0', 'schema', 'enum', '1'], | ||
severity: DiagnosticSeverity.Warning, | ||
}, | ||
], | ||
}, | ||
]); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,4 @@ extends: | |
- ./rulesets/IPA-102.yaml | ||
- ./rulesets/IPA-104.yaml | ||
- ./rulesets/IPA-109.yaml | ||
- ./rulesets/IPA-123.yaml |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# IPA-123: Enums | ||
# http://go/ipa/123 | ||
|
||
functions: | ||
- eachEnumValueMustBeUpperSnakeCase | ||
|
||
rules: | ||
xgen-IPA-123-enum-values-must-be-upper-snake-case: | ||
description: 'Enum values must be UPPER_SNAKE_CASE. http://go/ipa/123' | ||
message: '{{error}} http://go/ipa/123' | ||
severity: warn | ||
given: '$..enum' | ||
then: | ||
function: 'eachEnumValueMustBeUpperSnakeCase' |
29 changes: 29 additions & 0 deletions
29
tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { hasException } from './utils/exceptions.js'; | ||
import { getSchemaPath, resolveObject } from './utils/componentUtils.js'; | ||
import { casing } from '@stoplight/spectral-functions'; | ||
|
||
const RULE_NAME = 'xgen-IPA-123-enum-values-must-be-upper-snake-case'; | ||
const ERROR_MESSAGE = 'enum value must be UPPER_SNAKE_CASE.'; | ||
|
||
export default (input, _, { path, documentInventory }) => { | ||
const oas = documentInventory.resolved; | ||
const schemaPath = getSchemaPath(path); | ||
const schemaObject = resolveObject(oas, schemaPath); | ||
lovisaberggren marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (hasException(schemaObject, RULE_NAME)) { | ||
return; | ||
} | ||
|
||
const errors = []; | ||
input.forEach((enumValue, index) => { | ||
const isUpperSnakeCase = casing(enumValue, { type: 'macro' }); | ||
|
||
if (isUpperSnakeCase) { | ||
errors.push({ | ||
path: [...path, index], | ||
message: `${enumValue} ${ERROR_MESSAGE} `, | ||
}); | ||
} | ||
}); | ||
|
||
return errors; | ||
}; |
2 changes: 1 addition & 1 deletion
2
tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
tools/spectral/ipa/rulesets/functions/utils/componentUtils.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/** | ||
* Checks if a string belongs to a path parameter or a path parameter with a custom method. | ||
* | ||
* A path parameter has the format: `{paramName}` | ||
* A path parameter with a custom method has the format: `{paramName}:customMethod` | ||
* | ||
* @param {string} str - A string extracted from a path split by slashes. | ||
* @returns {boolean} True if the string matches the expected formats, false otherwise. | ||
*/ | ||
export function isPathParam(str) { | ||
const pathParamRegEx = new RegExp(`^{[a-z][a-zA-Z0-9]*}$`); | ||
const pathParamWithCustomMethodRegEx = new RegExp(`^{[a-z][a-zA-Z0-9]*}:[a-z][a-zA-Z0-9]*$`); | ||
return pathParamRegEx.test(str) || pathParamWithCustomMethodRegEx.test(str); | ||
} | ||
|
||
/** | ||
* Extracts the schema path from the given JSONPath array. | ||
* | ||
* This function is designed to handle two types of paths commonly encountered in OpenAPI definitions: | ||
* | ||
* 1. **Component Schema Paths**: | ||
* - Represented as: `components.schemas.schemaName.*.enum` | ||
* - This path indicates that the enum is defined within a schema under `components.schemas`. | ||
* - The function returns the first three elements (`["components", "schemas", "schemaName"]`). | ||
* | ||
* 2. **Parameter Schema Paths**: | ||
* - Represented as: `paths.*.method.parameters[*].schema.enum` | ||
* - This path indicates that the enum is part of a parameter's schema in an operation. | ||
* - The function identifies the location of `schema` in the path and returns everything up to (and including) it. | ||
* | ||
* @param {string[]} path - An array representing the JSONPath structure of the OpenAPI definition. | ||
* @returns {string[]} The truncated path pointing to the schema object. | ||
*/ | ||
export function getSchemaPath(path) { | ||
if (path.includes('components')) { | ||
return path.slice(0, 3); | ||
} else if (path.includes('paths')) { | ||
const index = path.findIndex((item) => item === 'schema'); | ||
return path.slice(0, index + 1); | ||
} | ||
} | ||
|
||
/** | ||
* Resolves the value of a nested property within an OpenAPI structure using a given path. | ||
* | ||
* This function traverses an OpenAPI object based on a specified path (array of keys) | ||
* and retrieves the value at the end of the path. If any key in the path is not found, | ||
* or the value is undefined at any point, the function will return `undefined`. | ||
* | ||
* @param {Object} oas - The entire OpenAPI Specification object. | ||
* @param {string[]} objectPath - An array of strings representing the path to the desired value. | ||
* For example, `['components', 'schemas', 'MySchema', 'properties']`. | ||
* @returns {*} The value at the specified path within the OpenAPI object, or `undefined` if the path is invalid. | ||
* | ||
* @example | ||
* const oas = { | ||
* components: { | ||
* schemas: { | ||
* MySchema: { | ||
* properties: { | ||
* fieldName: { type: 'string' } | ||
* } | ||
* } | ||
* } | ||
* } | ||
* }; | ||
* | ||
* const result = resolveObject(oas, ['components', 'schemas', 'MySchema', 'properties']); | ||
* console.log(result); // Output: { fieldName: { type: 'string' } } | ||
*/ | ||
export function resolveObject(oas, objectPath) { | ||
return objectPath.reduce((current, key) => { | ||
return current && current[key] ? current[key] : undefined; | ||
}, oas); | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.