Skip to content

Commit

Permalink
Validate order of superseded operations.
Browse files Browse the repository at this point in the history
Signed-off-by: dblock <[email protected]>
  • Loading branch information
dblock committed Jun 7, 2024
1 parent e6597c0 commit 1c2971a
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 9 deletions.
12 changes: 12 additions & 0 deletions tools/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ export function sort_by_keys (obj: Record<string, any>, priorities: string[] = [
})
}

export function sort_array_by_keys (values: any[], priorities: string[] = []): string[] {
const orders = _.fromPairs(priorities.map((k, i) => [k, i + 1]))
return _.clone(values).sort((a, b) => {
const order_a = orders[a]
const order_b = orders[b]
if (order_a != null && order_b != null) return order_a - order_b
if (order_a != null) return 1
if (order_b != null) return -1
return a.localeCompare(b)
})
}

export function ensure_parent_dir (file_path: string): void {
fs.mkdirSync(path.dirname(file_path), { recursive: true })
}
Expand Down
32 changes: 32 additions & 0 deletions tools/src/linter/components/SupersededOperationsFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,40 @@
* compatible open source license.
*/

import _ from 'lodash'
import { sort_array_by_keys } from '../../../helpers'
import FileValidator from './base/FileValidator'
import { type ValidationError } from 'types'
import { type OpenAPIV3 } from 'openapi-types'

const HTTP_METHODS = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'TRACE']

export default class SupersededOperationsFile extends FileValidator {
has_json_schema = true
protected _superseded_ops: OpenAPIV3.Document | undefined

validate (): ValidationError[] {
const schema_validations = super.validate()
if (schema_validations.length > 0) return schema_validations
return this.validate_order_of_operations()
}

superseded_ops (): OpenAPIV3.Document {
if (this._superseded_ops) return this._superseded_ops
this._superseded_ops = this.spec()
delete (this._superseded_ops as any).$schema
return this._superseded_ops
}

validate_order_of_operations (): ValidationError[] {
return _.entries(this.superseded_ops()).map(([path, p]) => {
const current_keys = p.operations
const sorted_keys = sort_array_by_keys(p.operations as string[], HTTP_METHODS)
if(!_.isEqual(current_keys, sorted_keys)) {
return this.error(
`Operations must be sorted. Expected ${_.join(sorted_keys, ', ')}.`,
path)
}
}).filter((e) => e) as ValidationError[]
}
}
49 changes: 41 additions & 8 deletions tools/tests/linter/SupersededOperationsFile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,45 @@

import SupersededOperationsFile from 'linter/components/SupersededOperationsFile'

test('validate()', () => {
const validator = new SupersededOperationsFile('./tools/tests/linter/fixtures/_superseded_operations.yaml')
expect(validator.validate()).toEqual([
{
file: 'fixtures/_superseded_operations.yaml',
message: "File content does not match JSON schema found in './json_schemas/_superseded_operations.schema.yaml':\n [\n {\n \"instancePath\": \"/~1hello~1world/operations/1\",\n \"schemaPath\": \"#/patternProperties/%5E~1/properties/operations/items/enum\",\n \"keyword\": \"enum\",\n \"params\": {\n \"allowedValues\": [\n \"GET\",\n \"POST\",\n \"PUT\",\n \"DELETE\",\n \"HEAD\",\n \"OPTIONS\",\n \"PATCH\"\n ]\n },\n \"message\": \"must be equal to one of the allowed values\"\n }\n]"
}
])
describe('validate()', () => {
test('invalid schema', () => {
const validator = new SupersededOperationsFile('./tools/tests/linter/fixtures/superseded_operations/invalid_schema.yaml')
expect(validator.validate()).toEqual([
{
file: 'superseded_operations/invalid_schema.yaml',
message: "File content does not match JSON schema found in './json_schemas/_superseded_operations.schema.yaml':\n " +
JSON.stringify([
{
"instancePath": "/~1hello~1world/operations/1",
"schemaPath": "#/patternProperties/%5E~1/properties/operations/items/enum",
"keyword": "enum",
"params": {
"allowedValues": [
"GET",
"POST",
"PUT",
"DELETE",
"HEAD",
"OPTIONS",
"PATCH"
]
},
"message": "must be equal to one of the allowed values"
}
], null, 2),
},
])
})

test('incorrect order of operations', () => {
const validator = new SupersededOperationsFile('./tools/tests/linter/fixtures/superseded_operations/incorrect_order_of_operations.yaml')
expect(validator.validate()).toEqual([
{
file: 'superseded_operations/incorrect_order_of_operations.yaml',
location: '/world/hello',
message: "Operations must be sorted. Expected GET, HEAD, POST, PUT, PATCH, DELETE."
},
])
})
})

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
$schema: ./json_schemas/_superseded_operations.schema.yaml

/world/hello:
superseded_by: /world/goodbye
operations:
- PATCH
- GET
- DELETE
- HEAD
- POST
- PUT
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ $schema: ./json_schemas/_superseded_operations.schema.yaml
superseded_by: /goodbye/world
operations:
- GET
- CLEAN
- CLEAN

0 comments on commit 1c2971a

Please sign in to comment.