Skip to content
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

Added validation for _info.yaml #281

Merged
merged 1 commit into from
May 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions json_schemas/_info.schema.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
$schema: http://json-schema.org/draft-07/schema#

type: object
properties:
title:
type: string
summary:
type: string
description:
type: string
termsOfService:
type: string
format: uri
contact:
$comment: https://spec.openapis.org/oas/v3.1.0#contact-object
type: object
properties:
name:
type: string
url:
type: string
format: uri
email:
type: string
format: email
license:
$comment: https://spec.openapis.org/oas/v3.1.0#license-object
type: object
properties:
name:
type: string
identifier:
type: string
url:
type: string
format: uri
required:
- name
version:
type: string
required:
- title
- version
- $schema
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
$schema: http://json-schema.org/draft-07/schema#

type: object
patternProperties:
^\$schema$:
Expand Down
5 changes: 3 additions & 2 deletions spec/_info.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
title: OpenSearch API
description: OpenSearch API
$schema: ../json_schemas/_info.schema.yaml

title: OpenSearch API Specification
version: 1.0.0
2 changes: 1 addition & 1 deletion spec/_superseded_operations.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
$schema: ../json_schemas/_superseded_operations.yaml
$schema: ../json_schemas/_superseded_operations.schema.yaml

/_opendistro/_alerting/destinations:
superseded_by: /_plugins/_alerting/destinations
Expand Down
6 changes: 4 additions & 2 deletions tools/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ export function sort_by_keys (obj: Record<string, any>, priorities: string[] = [
})
}

export function read_yaml (file_path: string): Record<string, any> {
return YAML.parse(fs.readFileSync(file_path, 'utf8'))
export function read_yaml (file_path: string, exclude_schema: boolean = false): Record<string, any> {
const doc = YAML.parse(fs.readFileSync(file_path, 'utf8'))
if (exclude_schema) delete doc.$schema
return doc
}

export function write_yaml (file_path: string, content: Record<string, any>): void {
Expand Down
10 changes: 7 additions & 3 deletions tools/linter/SpecValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import NamespacesFolder from './components/NamespacesFolder'
import { type ValidationError } from '../types'
import SchemaRefsValidator from './SchemaRefsValidator'
import SupersededOperationsFile from './components/SupersededOperationsFile'
import InfoFile from './components/InfoFile'

export default class SpecValidator {
superseded_ops_files: SupersededOperationsFile
superseded_ops_file: SupersededOperationsFile
info_file: InfoFile
namespaces_folder: NamespacesFolder
schemas_folder: SchemasFolder
schema_refs_validator: SchemaRefsValidator

constructor (root_folder: string) {
this.superseded_ops_files = new SupersededOperationsFile(`${root_folder}/_superseded_operations.yaml`)
this.superseded_ops_file = new SupersededOperationsFile(`${root_folder}/_superseded_operations.yaml`)
this.info_file = new InfoFile(`${root_folder}/_info.yaml`)
this.namespaces_folder = new NamespacesFolder(`${root_folder}/namespaces`)
this.schemas_folder = new SchemasFolder(`${root_folder}/schemas`)
this.schema_refs_validator = new SchemaRefsValidator(this.namespaces_folder, this.schemas_folder)
Expand All @@ -26,7 +29,8 @@ export default class SpecValidator {

return [
...this.schema_refs_validator.validate(),
...this.superseded_ops_files.validate()
...this.superseded_ops_file.validate(),
...this.info_file.validate()
]
}
}
5 changes: 5 additions & 0 deletions tools/linter/components/InfoFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import FileValidator from './base/FileValidator'

export default class InfoFile extends FileValidator {
has_json_schema = true
}
19 changes: 1 addition & 18 deletions tools/linter/components/SupersededOperationsFile.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,5 @@
import FileValidator from './base/FileValidator'
import ajv from 'ajv'
import { type ValidationError } from '../../types'
import { read_yaml } from '../../helpers'

export default class SupersededOperationsFile extends FileValidator {
readonly JSON_SCHEMA_PATH = '../json_schemas/_superseded_operations.yaml'

validate (): ValidationError[] {
return [
this.validate_json_schema()
].filter(e => e) as ValidationError[]
}

validate_json_schema (): ValidationError | undefined {
const schema = read_yaml(this.JSON_SCHEMA_PATH)
const validator = (new ajv()).compile(schema)
if (!validator(this.spec())) {
return this.error(`File content does not match JSON schema found in '${this.JSON_SCHEMA_PATH}':\n ${JSON.stringify(validator.errors, null, 2)}`)
}
}
has_json_schema = true
}
20 changes: 19 additions & 1 deletion tools/linter/components/base/FileValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import ValidatorBase from './ValidatorBase'
import { type ValidationError } from '../../../types'
import { type OpenAPIV3 } from 'openapi-types'
import { read_yaml } from '../../../helpers'
import AJV from 'ajv'
import addFormats from 'ajv-formats'

export default class FileValidator extends ValidatorBase {
file_path: string
has_json_schema: boolean = false
protected _spec: OpenAPIV3.Document | undefined

constructor (file_path: string) {
Expand All @@ -23,11 +26,13 @@ export default class FileValidator extends ValidatorBase {
if (extension_error) return [extension_error]
const yaml_error = this.validate_yaml()
if (yaml_error) return [yaml_error]
const json_schema_error = this.validate_json_schema()
if (json_schema_error) return [json_schema_error]
return this.validate_file()
}

validate_file (): ValidationError[] {
throw new Error('Method not implemented.')
return []
}

validate_extension (): ValidationError | undefined {
Expand All @@ -41,4 +46,17 @@ export default class FileValidator extends ValidatorBase {
return this.error('Unable to read or parse YAML.', 'File Content')
}
}

validate_json_schema (): ValidationError | undefined {
if (!this.has_json_schema) return
const json_schema_path: string = (this.spec() as any).$schema ?? ''
if (json_schema_path === '') return this.error('JSON Schema is required but not found in this file.', '$schema')
const schema = read_yaml(json_schema_path)
const ajv = new AJV({ schemaId: 'id' })
addFormats(ajv)
const validator = ajv.compile(schema)
if (!validator(this.spec())) {
return this.error(`File content does not match JSON schema found in '${json_schema_path}':\n ${JSON.stringify(validator.errors, null, 2)}`)
}
}
}
2 changes: 1 addition & 1 deletion tools/merger/OpenApiMerger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default class OpenApiMerger {
this.root_folder = fs.realpathSync(root_folder)
this.spec = {
openapi: '3.1.0',
info: read_yaml(`${this.root_folder}/_info.yaml`),
info: read_yaml(`${this.root_folder}/_info.yaml`, true),
paths: {},
components: {
parameters: {},
Expand Down
17 changes: 17 additions & 0 deletions tools/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@types/lodash": "^4.14.202",
"@types/node": "^20.10.3",
"ajv": "^8.13.0",
"ajv-formats": "^3.0.1",
"lodash": "^4.17.21",
"ts-node": "^10.9.1",
"typescript": "^5.4.5",
Expand Down
12 changes: 12 additions & 0 deletions tools/test/linter/InfoFile.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import InfoFile from '../../linter/components/InfoFile'

test('validate()', () => {
const validator = new InfoFile('./test/linter/fixtures/_info.yaml')
expect(validator.validate()).toEqual([
{
file: 'fixtures/_info.yaml',
location: '$schema',
message: 'JSON Schema is required but not found in this file.'
}
])
})
2 changes: 1 addition & 1 deletion tools/test/linter/SupersededOperationsFile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ test('validate()', () => {
expect(validator.validate()).toEqual([
{
file: 'fixtures/_superseded_operations.yaml',
message: "File content does not match JSON schema found in '../json_schemas/_superseded_operations.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]"
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]"
}
])
})
2 changes: 2 additions & 0 deletions tools/test/linter/fixtures/_info.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
title: OpenSearch API Specification
version: 1.0.0
2 changes: 1 addition & 1 deletion tools/test/linter/fixtures/_superseded_operations.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
$schema: ../../../../json_schemas/_superseded_operations.yaml
$schema: ../json_schemas/_superseded_operations.schema.yaml

/hello/world:
superseded_by: /goodbye/world
Expand Down
4 changes: 4 additions & 0 deletions tools/test/linter/fixtures/empty/_info.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
$schema: ../json_schemas/_info.schema.yaml

title: ''
version: ''
Original file line number Diff line number Diff line change
@@ -1 +1 @@
$schema: ../../../../../json_schemas/_superseded_operations.yaml
$schema: ../json_schemas/_superseded_operations.schema.yaml
2 changes: 2 additions & 0 deletions tools/test/merger/fixtures/spec/_info.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
$schema: should-be-ignored

title: OpenSearch API
description: OpenSearch API
version: 1.0.0
Loading