Skip to content

Commit 05e9bba

Browse files
authored
feat!: explicitly provide ref error type (#9)
* feat!: explicitly provide ref error type * build: add additional branches on push * feat: provide additional details in richRefObjectNotAllowed error message
1 parent f556de6 commit 05e9bba

File tree

5 files changed

+28
-14
lines changed

5 files changed

+28
-14
lines changed

src/define-origins-and-resolve-ref.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
NormalizationRule,
1919
OriginCache,
2020
OriginsMetaRecord,
21+
RefErrorTypes,
2122
ResolveOptions,
2223
RichReference,
2324
} from './types'
@@ -164,15 +165,15 @@ const createDefineOriginsAndResolveRefHook: (rootJso: unknown, options: Internal
164165
value: JSON_SCHEMA_PROPERTY_REF,
165166
}, state.originCache) //abuse cache. Keys should be a real node value only!!!
166167
if (typeof $ref !== 'string') {
167-
options.onRefResolveError?.(ErrorMessage.refNotValidFormat($ref), path, $ref)
168+
options.onRefResolveError?.(ErrorMessage.refNotValidFormat($ref), path, $ref, RefErrorTypes.REF_NOT_VALID_FORMAT)
168169
const brokenValueClone = { [JSON_SCHEMA_PROPERTY_REF]: $ref }
169170
state.node[safeKey] = brokenValueClone
170171
setOrigins(state.node, safeKey, options.originsFlag, [originForObj])
171172
setOrigins(brokenValueClone, JSON_SCHEMA_PROPERTY_REF, options.originsFlag, [originForRef])
172173
return { done: true }
173174
}
174175
if (!options.richRefAllowed && Reflect.ownKeys(sibling).length !== 0) {
175-
options.onRefResolveError?.(ErrorMessage.richRefObjectNotAllowed(), path, $ref)
176+
options.onRefResolveError?.(ErrorMessage.richRefObjectNotAllowed($ref), path, $ref, RefErrorTypes.RICH_REF_NOT_ALLOWED)
176177
sibling = {}
177178
}
178179
const reference = parseRef($ref)
@@ -342,7 +343,7 @@ const createDefineOriginsAndResolveRefHook: (rootJso: unknown, options: Internal
342343
const { refValue, origin } = refInSourceJso
343344
return wrapRefWithAllOfIfNeed(refValue, sibling, origin)
344345
}
345-
options.onRefResolveError?.(ErrorMessage.refNotFound($ref), path, $ref)
346+
options.onRefResolveError?.(ErrorMessage.refNotFound($ref), path, $ref, RefErrorTypes.REF_NOT_FOUND)
346347
const brokenValueClone = { [JSON_SCHEMA_PROPERTY_REF]: $ref }
347348
state.node[safeKey] = brokenValueClone
348349
setOrigins(state.node, safeKey, options.originsFlag, [originForObj])

src/errors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export const ErrorMessage = {
44
mergeError: () => 'Could not merge values, they are probably incompatible',
55
mergeWithBrokenRef: () => 'Could not merge values with unresolved ref',
66
ruleNotFound: (key: any) => `Merge rule not found for key: ${key}`,
7-
richRefObjectNotAllowed: () => `${JSON_SCHEMA_PROPERTY_REF} can't have siblings in this specification version`,
7+
richRefObjectNotAllowed: (ref: string) => `${JSON_SCHEMA_PROPERTY_REF} can't have siblings in this specification version: ${ref}`,
88
refNotFound: (ref: string) => `${JSON_SCHEMA_PROPERTY_REF} can't be resolved: ${ref}`,
99
refNotValidFormat: (ref: string) => `${JSON_SCHEMA_PROPERTY_REF} can't be parsed: ${ref}`,
1010
} as const

src/types.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ export type JsonSchema = Record<PropertyKey, unknown>
77

88
export type NormalizationRules = CrawlRules<NormalizationRule>
99

10+
export const RefErrorTypes = {
11+
RICH_REF_NOT_ALLOWED: 'richRefObjectNotAllowed' as const,
12+
REF_NOT_FOUND: 'refNotFound' as const,
13+
REF_NOT_VALID_FORMAT: 'refNotValidFormat' as const,
14+
} as const
15+
16+
export type RefErrorType = typeof RefErrorTypes[keyof typeof RefErrorTypes]
17+
1018
//todo ugly API. Some Flag activate transformation (like syntheticTitleFlag) some flags just mark result for transfomration
1119
export interface ResolveOptions {
1220
resolveRef?: boolean // execute resolve ref phase (inline usages that can produce cycled JSO)
@@ -17,7 +25,7 @@ export interface ResolveOptions {
1725
originsFlag?: symbol // used in JSO as anchor to chained declaration path (contains link to parent in declarationPath)
1826
originsAlreadyDefined?: boolean // are there already origins in the spec
1927
ignoreSymbols?: symbol[], // symbols to ignore scan
20-
onRefResolveError?: (message: string, path: JsonPath, ref: string) => void
28+
onRefResolveError?: (message: string, path: JsonPath, ref: string, errorType: RefErrorType) => void
2129
}
2230

2331
export interface MergeOptions {

test/oas/merge.errors.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { JsonPath } from '@netcracker/qubership-apihub-json-crawl'
2-
import { normalize } from '../../src'
2+
import { normalize, RefErrorType, RefErrorTypes } from '../../src'
33

44
describe("merge errors handling", function () {
55
it('should trigger onRefResolveError when merging broken $ref', (done) => {
66

7-
const onRefResolveError = (message: string, path: JsonPath, ref: string) => {
7+
const onRefResolveError = (message: string, path: JsonPath, ref: string, errorType: RefErrorType) => {
88

9-
expect(ref).toBe ("#/foo")
9+
expect(ref).toBe("#/foo")
10+
expect(errorType).toBe(RefErrorTypes.REF_NOT_FOUND)
1011
done()
1112
}
1213

test/oas/merge.openapi.test.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { normalize } from '../../src'
1+
import { normalize, RefErrorType, RefErrorTypes } from '../../src'
22
import source31x from '../resources/openapi31x.json'
33
import source30x from '../resources/openapi30x.json'
44
import { JsonPath } from '@netcracker/qubership-apihub-json-crawl'
@@ -7,7 +7,8 @@ import { ErrorMessage } from '../../src/errors'
77
interface Error {
88
readonly message: string,
99
readonly path: JsonPath,
10-
readonly ref: unknown
10+
readonly ref: unknown,
11+
readonly errorType: RefErrorType
1112
}
1213

1314
describe('merge allof in openapi schema', function () {
@@ -128,7 +129,7 @@ describe('merge allof in openapi schema', function () {
128129
const errors: Error[] = []
129130
const result = normalize({ ...documentFragment, openapi: '3.0.0' }, {
130131
source: documentSource,
131-
onRefResolveError: (message, path, ref) => errors.push({ message, path, ref: ref }),
132+
onRefResolveError: (message, path, ref, errorType) => errors.push({ message, path, ref: ref, errorType }),
132133
})
133134
const expected = {
134135
openapi: '3.0.0',
@@ -170,19 +171,22 @@ describe('merge allof in openapi schema', function () {
170171
expect(result).toEqual(expected)
171172
expect(errors).toEqual([
172173
{
173-
message: ErrorMessage.richRefObjectNotAllowed(),
174+
message: ErrorMessage.richRefObjectNotAllowed('#/components/schemas/Human'),
174175
path: ['paths', 'humans', 'get', 'responses', '200', 'content', 'application/json', 'schema', 'items'],
175176
ref: '#/components/schemas/Human',
177+
errorType: RefErrorTypes.RICH_REF_NOT_ALLOWED
176178
},
177179
{
178-
message: ErrorMessage.richRefObjectNotAllowed(),
180+
message: ErrorMessage.richRefObjectNotAllowed('#/components/schemas/Location'),
179181
path: ['paths', 'humans', 'get', 'responses', '200', 'content', 'application/json', 'schema', 'items', 'properties', 'location'],
180182
ref: '#/components/schemas/Location',
183+
errorType: RefErrorTypes.RICH_REF_NOT_ALLOWED
181184
},
182185
{
183-
message: ErrorMessage.richRefObjectNotAllowed(),
186+
message: ErrorMessage.richRefObjectNotAllowed('#/components/schemas/Human'),
184187
path: ['paths', 'humans', 'get', 'responses', '200', 'content', 'application/json', 'schema', 'items', 'properties', 'location', 'allOf', 0, 'properties', 'ownedBy'],
185188
ref: '#/components/schemas/Human',
189+
errorType: RefErrorTypes.RICH_REF_NOT_ALLOWED
186190
},
187191
] as Error[])
188192
})

0 commit comments

Comments
 (0)