Skip to content

Commit

Permalink
fix(schema): configure missingRefs
Browse files Browse the repository at this point in the history
  • Loading branch information
P0lip committed May 7, 2020
1 parent e5fe1dc commit 2a0f080
Show file tree
Hide file tree
Showing 8 changed files with 18 additions and 29 deletions.
19 changes: 0 additions & 19 deletions src/__tests__/linter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { RuleCollection, Spectral } from '../spectral';

const invalidSchema = JSON.stringify(require('./__fixtures__/petstore.invalid-schema.oas3.json'));
const studioFixture = JSON.stringify(require('./__fixtures__/studio-default-fixture-oas3.json'), null, 2);
const todosInvalid = JSON.stringify(require('./__fixtures__/todos.invalid.oas2.json'));
const petstoreMergeKeys = JSON.stringify(require('./__fixtures__/petstore.merge.keys.oas3.json'));

const fnName = 'fake';
Expand Down Expand Up @@ -655,24 +654,6 @@ responses:: !!foo
]);
});

test('should report invalid schema $refs', async () => {
spectral.registerFormat('oas2', isOpenApiv2);
spectral.registerFormat('oas3', isOpenApiv3);
await spectral.loadRuleset('spectral:oas');

const result = await spectral.run(todosInvalid);

expect(result).toEqual(
expect.arrayContaining([
expect.objectContaining({
code: 'oas2-valid-parameter-example',
message: "can't resolve reference #/parameters/missing from id #",
path: ['paths', '/todos/{todoId}', 'put', 'parameters', '1', 'schema', 'example'],
}),
]),
);
});

test('should report invalid $refs', async () => {
const result = await spectral.run(invalidSchema);

Expand Down
2 changes: 2 additions & 0 deletions src/functions/__tests__/alphabetical.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ function runAlphabetical(target: any, keyedBy?: string) {
given: null,
original: null,
documentInventory: new DocumentInventory(new Document(safeStringify(target), Parsers.Json), {} as any),
context: 'resolved',
},
);
}
Expand Down Expand Up @@ -118,6 +119,7 @@ describe('alphabetical', () => {
given: null,
original: null,
documentInventory: new DocumentInventory(document, {} as any),
context: 'resolved',
},
),
).toEqual([
Expand Down
2 changes: 1 addition & 1 deletion src/functions/__tests__/casing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ function runCasing(target: unknown, type: CasingType, disallowDigits?: boolean,
target,
{ type, disallowDigits, separator },
{ given: ['$'] },
{ given: null, original: null, documentInventory: {} as any },
{ given: null, original: null, documentInventory: {} as any, context: 'resolved' },
);
}

Expand Down
14 changes: 9 additions & 5 deletions src/functions/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const logger = {

const ajvInstances = {};

function getAjv(oasVersion: Optional<number>, allErrors: Optional<boolean>): AJV.Ajv {
function getAjv(oasVersion: Optional<number>, allErrors: Optional<boolean>, shouldReportMissingRefs: boolean): AJV.Ajv {
const type: string = oasVersion && oasVersion >= 2 ? 'oas' + oasVersion : 'jsonschema';
if (typeof ajvInstances[type] !== 'undefined') {
return ajvInstances[type];
Expand All @@ -61,6 +61,10 @@ function getAjv(oasVersion: Optional<number>, allErrors: Optional<boolean>): AJV
schemaId: 'auto',
allErrors,
jsonPointers: true,
// let's ignore any $ref errors if schema fn is provided with already resolved content,
// if our resolver fails to resolve them,
// ajv is unlikely to do it either, since it won't have access to the whole document, but a small portion of it
missingRefs: shouldReportMissingRefs ? 'fail' : 'ignore',
unknownFormats: 'ignore',
nullable: oasVersion === 3, // Support nullable for OAS3
logger,
Expand Down Expand Up @@ -91,8 +95,8 @@ function getSchemaId(schemaObj: JSONSchema): void | string {
}

const validators = new (class extends WeakMap<JSONSchema, ValidateFunction> {
public get({ schema: schemaObj, oasVersion, allErrors }: ISchemaOptions) {
const ajv = getAjv(oasVersion, allErrors);
public assign({ schema: schemaObj, oasVersion, allErrors }: ISchemaOptions, context: string) {
const ajv = getAjv(oasVersion, allErrors, context === 'unresolved');
const schemaId = getSchemaId(schemaObj);
let validator = schemaId !== void 0 ? ajv.getSchema(schemaId) : void 0;
if (validator !== void 0) {
Expand Down Expand Up @@ -143,7 +147,7 @@ const cleanAJVErrorMessage = (message: string, path: Optional<string>, suggestio
}`;
};

export const schema: ISchemaFunction = (targetVal, opts, paths) => {
export const schema: ISchemaFunction = (targetVal, opts, paths, { context }) => {
const results: IFunctionResult[] = [];

const path = paths.target || paths.given;
Expand All @@ -162,7 +166,7 @@ export const schema: ISchemaFunction = (targetVal, opts, paths) => {

try {
// we used the compiled validation now, hence this lookup here (see the logic above for more info)
const validator = opts.ajv ?? validators.get(opts);
const validator = opts.ajv ?? validators.assign(opts, context);
if (!validator(targetVal) && validator.errors) {
opts.prepareResults?.(validator.errors);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function runPayloadValidation(targetVal: any, field: string) {
targetVal,
{ field },
{ given: ['$', 'components', 'messages', 'aMessage'] },
{ given: null, original: null, documentInventory: {} as any },
{ given: null, original: null, documentInventory: {} as any, context: 'resolved' },
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/rulesets/oas/functions/__tests__/typedEnum.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function runTypedEnum(targetVal: any, reportingThreshold: any) {
targetVal,
{ reportingThreshold },
{ given: ['$'] },
{ given: null, original: null, documentInventory: {} as any },
{ given: null, original: null, documentInventory: {} as any, context: 'resolved' },
);
}

Expand Down
5 changes: 3 additions & 2 deletions src/runner/lintNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Document } from '../document';
import { Rule } from '../rule';
import { IMessageVars, message } from '../rulesets/message';
import { getDiagnosticSeverity } from '../rulesets/severity';
import { IFunctionResult, IGivenNode } from '../types';
import { IFunctionResult, IFunctionValues, IGivenNode } from '../types';
import { decodeSegmentFragment, getClosestJsonPath, printPath, PrintStyle } from '../utils';
import { IRunnerInternalContext } from './types';
import { getLintTargets, IExceptionLocation, isAKnownException } from './utils';
Expand All @@ -16,10 +16,11 @@ export const lintNode = (
rule: Rule,
exceptionLocations: Optional<IExceptionLocation[]>,
): void => {
const fnContext = {
const fnContext: IFunctionValues = {
original: node.value,
given: node.value,
documentInventory: context.documentInventory,
context: rule.resolved ? 'resolved' : 'unresolved',
};

const givenPath = node.path.length > 0 && node.path[0] === '$' ? node.path.slice(1) : node.path;
Expand Down
1 change: 1 addition & 0 deletions src/types/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface IFunctionValues {
original: any;
given: any;
documentInventory: DocumentInventory;
context: 'resolved' | 'unresolved';
}

export interface IFunctionResult {
Expand Down

0 comments on commit 2a0f080

Please sign in to comment.