Skip to content

Commit

Permalink
Add linter for numeric types
Browse files Browse the repository at this point in the history
Signed-off-by: Thomas Farr <[email protected]>
  • Loading branch information
Xtansia committed Oct 2, 2024
1 parent b8a32bf commit d15ee53
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 67 deletions.
5 changes: 5 additions & 0 deletions .cspell
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ kstem
kuromoji
Kuromoji
languageset
latlon
Léon
localstats
Lovins
Expand Down Expand Up @@ -160,6 +161,8 @@ securityconfig
slowlog
Slowlog
slurpfile
smartcn
Smartcn
snapshotted
softmax
Sorani
Expand All @@ -179,9 +182,11 @@ termvectors
tfidf
Tfidf
Tika
tlbr
tokenfilters
translog
Translog
trbl
tubone
Undeploys
unigrams
Expand Down
2 changes: 1 addition & 1 deletion spec/schemas/_common.analysis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1373,7 +1373,7 @@ components:
type: string
enum:
- canonical
- 'no'
- no
IcuCollationStrength:
type: string
enum:
Expand Down
8 changes: 4 additions & 4 deletions spec/schemas/_common.mapping.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -881,17 +881,17 @@ components:
- title: left
type: string
enum:
- left
- LEFT
- clockwise
- cw
- left
- title: right
type: string
enum:
- right
- RIGHT
- counterclockwise
- ccw
- counterclockwise
- right
GeoStrategy:
type: string
enum:
Expand Down Expand Up @@ -1201,8 +1201,8 @@ components:
model_id:
type: string
required:
- type
- dimension
- type
MatchType:
type: string
enum:
Expand Down
4 changes: 2 additions & 2 deletions spec/schemas/indices._common.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -418,10 +418,10 @@ components:
TranslogDurability:
type: string
enum:
- async
- ASYNC
- request
- REQUEST
- async
- request
TranslogRetention:
type: object
properties:
Expand Down
3 changes: 3 additions & 0 deletions tools/src/_utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import { to_json } from "../helpers";
export const HTTP_METHODS: OpenAPIV3.HttpMethods[] = Object.values(OpenAPIV3.HttpMethods)
export type SchemaObjectType = OpenAPIV3.ArraySchemaObjectType | OpenAPIV3.NonArraySchemaObjectType
export const SCHEMA_OBJECT_TYPES: SchemaObjectType[] = ['array', 'boolean', 'object', 'number', 'string', 'integer']
export const SCHEMA_NUMERIC_TYPES: SchemaObjectType[] = ['number', 'integer']
export const SCHEMA_NUMBER_FORMATS: string[] = ['float', 'double']
export const SCHEMA_INTEGER_FORMATS: string[] = ['int32', 'int64']

export function is_ref<O extends object> (o: MaybeRef<O>): o is OpenAPIV3.ReferenceObject {
return o != null && typeof o === 'object' && '$ref' in o
Expand Down
55 changes: 0 additions & 55 deletions tools/src/linter/InlineObjectSchemaValidator.ts

This file was deleted.

99 changes: 99 additions & 0 deletions tools/src/linter/SchemaVisitingValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

import type NamespacesFolder from './components/NamespacesFolder'
import type SchemasFolder from './components/SchemasFolder'
import { type ValidationError } from 'types'
import { SchemaVisitor } from '../_utils/SpecificationVisitor'
import {
is_ref,
type MaybeRef,
SCHEMA_INTEGER_FORMATS,
SCHEMA_NUMBER_FORMATS,
SCHEMA_NUMERIC_TYPES,
SpecificationContext
} from '../_utils'
import { type OpenAPIV3 } from 'openapi-types'

export default class SchemaVisitingValidator {
private readonly _namespaces_folder: NamespacesFolder
private readonly _schemas_folder: SchemasFolder

constructor (namespaces_folder: NamespacesFolder, schemas_folder: SchemasFolder) {
this._namespaces_folder = namespaces_folder
this._schemas_folder = schemas_folder
}

validate (): ValidationError[] {
const errors: ValidationError[] = []

const validating_functions = [
this.#validate_inline_object_schema.bind(this),
this.#validate_numeric_schema.bind(this)
]

const visitor = new SchemaVisitor((ctx, schema) => {
for (const f of validating_functions) {
f(ctx, schema, errors)
}
});

[
...this._namespaces_folder.files,
...this._schemas_folder.files
].forEach(f => { visitor.visit_specification(new SpecificationContext(f.file), f.spec()) })

return errors
}

#validate_inline_object_schema (ctx: SpecificationContext, schema: MaybeRef<OpenAPIV3.SchemaObject>, errors: ValidationError[]): void {
if (is_ref(schema) || schema.type !== 'object' || schema.properties === undefined) {
return
}

const ancestry = ctx.keys.reverse()

if (ancestry[1] === 'properties' ||
ancestry[0] === 'additionalProperties' ||
ancestry[0] === 'items' ||
(ancestry[0] === 'schema' && ancestry[2] === 'parameters' && ancestry[3] !== 'components')) {
errors.push(ctx.error('object schemas should be defined out-of-line via a $ref'))
}
}

#validate_numeric_schema (ctx: SpecificationContext, schema: MaybeRef<OpenAPIV3.SchemaObject>, errors: ValidationError[]): void {
if (is_ref(schema) || schema.type === undefined || !SCHEMA_NUMERIC_TYPES.includes(schema.type)) {
return
}

if (schema.type === 'number') {
if (schema.format === undefined || SCHEMA_NUMBER_FORMATS.includes(schema.format)) {
return
}

if (SCHEMA_INTEGER_FORMATS.includes(schema.format)) {
errors.push(ctx.error(`schema of type 'number' with format '${schema.format}' should instead be of type 'integer'`))
} else {
errors.push(ctx.error(`schema of type 'number' with format '${schema.format}' is invalid, expected one of: ${SCHEMA_NUMBER_FORMATS.join(', ')}`))
}
}

if (schema.type === 'integer') {
if (schema.format === undefined || SCHEMA_INTEGER_FORMATS.includes(schema.format)) {
return
}

if (SCHEMA_NUMBER_FORMATS.includes(schema.format)) {
errors.push(ctx.error(`schema of type 'integer' with format '${schema.format}' should instead be of type 'number'`))
} else {
errors.push(ctx.error(`schema of type 'integer' with format '${schema.format}' is invalid, expected one of: ${SCHEMA_INTEGER_FORMATS.join(', ')}`))
}
}
}
}
6 changes: 3 additions & 3 deletions tools/src/linter/SpecValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { type ValidationError } from 'types'
import SchemaRefsValidator from './SchemaRefsValidator'
import SupersededOperationsFile from './components/SupersededOperationsFile'
import InfoFile from './components/InfoFile'
import InlineObjectSchemaValidator from './InlineObjectSchemaValidator'
import SchemaVisitingValidator from './SchemaVisitingValidator'
import SchemasValidator from './SchemasValidator'
import { type Logger } from '../Logger'

Expand All @@ -25,7 +25,7 @@ export default class SpecValidator {
schemas_folder: SchemasFolder
schemas_validator: SchemasValidator
schema_refs_validator: SchemaRefsValidator
inline_object_schema_validator: InlineObjectSchemaValidator
inline_object_schema_validator: SchemaVisitingValidator

constructor (root_folder: string, logger: Logger) {
this.logger = logger
Expand All @@ -35,7 +35,7 @@ export default class SpecValidator {
this.schemas_folder = new SchemasFolder(`${root_folder}/schemas`)
this.schemas_validator = new SchemasValidator(root_folder, logger)
this.schema_refs_validator = new SchemaRefsValidator(this.namespaces_folder, this.schemas_folder)
this.inline_object_schema_validator = new InlineObjectSchemaValidator(this.namespaces_folder, this.schemas_folder)
this.inline_object_schema_validator = new SchemaVisitingValidator(this.namespaces_folder, this.schemas_folder)
}

validate (): ValidationError[] {
Expand Down
4 changes: 2 additions & 2 deletions tools/tests/linter/InlineObjectSchemaValidator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@

import SchemasFolder from 'linter/components/SchemasFolder'
import NamespacesFolder from 'linter/components/NamespacesFolder'
import InlineObjectSchemaValidator from 'linter/InlineObjectSchemaValidator'
import SchemaVisitingValidator from 'linter/SchemaVisitingValidator'

test('validate()', () => {
const root_folder = './tools/tests/linter/fixtures/inline_object_schema_validator'
const namespaces_folder = new NamespacesFolder(`${root_folder}/namespaces`)
const schemas_folder = new SchemasFolder(`${root_folder}/schemas`)
const validator = new InlineObjectSchemaValidator(namespaces_folder, schemas_folder)
const validator = new SchemaVisitingValidator(namespaces_folder, schemas_folder)
expect(validator.validate()).toEqual([
{
file: 'namespaces/ops.yaml',
Expand Down

0 comments on commit d15ee53

Please sign in to comment.