-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(json-schema-parser): add dereferenceJsonSchema
- Loading branch information
1 parent
c567b1e
commit 451db1e
Showing
6 changed files
with
270 additions
and
5 deletions.
There are no files selected for viewing
186 changes: 186 additions & 0 deletions
186
...arser/json-schema-parser/src/jsonSchema/202012/calculations/dereferenceJsonSchema.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
import { afterAll, beforeAll, describe, expect, it, jest } from '@jest/globals'; | ||
|
||
jest.mock('../actions/traverseJsonSchema'); | ||
jest.mock('./getJsonSchemaBaseUri'); | ||
|
||
import { | ||
JsonSchema, | ||
JsonSchemaObject, | ||
} from '@cuaklabs/json-schema-types/2020-12'; | ||
|
||
import { traverseJsonSchema } from '../actions/traverseJsonSchema'; | ||
import { JsonRootSchemaFixtures } from '../fixtures/JsonRootSchemaFixtures'; | ||
import { DereferenceFunction } from '../models/DereferenceFunction'; | ||
import { TraverseJsonSchemaCallbackParams } from '../models/TraverseJsonSchemaCallbackParams'; | ||
import { TraverseJsonSchemaParams } from '../models/TraverseJsonSchemaParams'; | ||
import { UriOptions } from '../models/UriOptions'; | ||
import { dereferenceJsonSchema } from './dereferenceJsonSchema'; | ||
import { getJsonSchemaBaseUri } from './getJsonSchemaBaseUri'; | ||
|
||
describe(dereferenceJsonSchema.name, () => { | ||
let derefMock: jest.Mock<DereferenceFunction>; | ||
let schemaFixture: JsonSchema; | ||
let uriOptionsFixture: UriOptions; | ||
|
||
beforeAll(() => { | ||
derefMock = jest.fn(); | ||
schemaFixture = JsonRootSchemaFixtures.any; | ||
uriOptionsFixture = {}; | ||
}); | ||
|
||
describe('when called, and traverseJsonSchema() does not call callback', () => { | ||
let baseUriFixture: string; | ||
let referenceMapFixture: Map<string, JsonSchema>; | ||
|
||
let result: unknown; | ||
|
||
beforeAll(async () => { | ||
baseUriFixture = 'base://fixture'; | ||
referenceMapFixture = new Map(); | ||
|
||
( | ||
getJsonSchemaBaseUri as jest.Mock<typeof getJsonSchemaBaseUri> | ||
).mockReturnValueOnce(baseUriFixture); | ||
|
||
( | ||
traverseJsonSchema as jest.Mock<typeof traverseJsonSchema> | ||
).mockReturnValueOnce(undefined); | ||
|
||
result = await dereferenceJsonSchema( | ||
derefMock, | ||
schemaFixture, | ||
referenceMapFixture, | ||
uriOptionsFixture, | ||
); | ||
}); | ||
|
||
afterAll(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should call getJsonSchemaBaseUri()', () => { | ||
expect(getJsonSchemaBaseUri).toHaveBeenCalledTimes(1); | ||
expect(getJsonSchemaBaseUri).toHaveBeenCalledWith( | ||
schemaFixture, | ||
uriOptionsFixture, | ||
); | ||
}); | ||
|
||
it('should call traverseJsonSchema()', () => { | ||
const expectedParams: TraverseJsonSchemaParams = { | ||
schema: schemaFixture, | ||
}; | ||
|
||
expect(traverseJsonSchema).toHaveBeenCalledTimes(1); | ||
expect(traverseJsonSchema).toHaveBeenCalledWith( | ||
expectedParams, | ||
expect.any(Function), | ||
); | ||
}); | ||
|
||
it('should resolve to undefined', () => { | ||
expect(result).toBeUndefined(); | ||
}); | ||
}); | ||
|
||
describe('when called, and traverseJsonSchema() calls callback twice with an schema with a reference', () => { | ||
let dereferencedSchemaFixture: JsonSchema; | ||
let subSchemaFixture: JsonSchemaObject; | ||
let baseUriFixture: string; | ||
let referenceMapFixture: Map<string, JsonSchema>; | ||
|
||
let result: unknown; | ||
|
||
beforeAll(async () => { | ||
dereferencedSchemaFixture = JsonRootSchemaFixtures.any; | ||
subSchemaFixture = JsonRootSchemaFixtures.withRef; | ||
baseUriFixture = 'base://fixture'; | ||
referenceMapFixture = new Map(); | ||
|
||
( | ||
getJsonSchemaBaseUri as jest.Mock<typeof getJsonSchemaBaseUri> | ||
).mockReturnValueOnce(baseUriFixture); | ||
|
||
(traverseJsonSchema as jest.Mock<typeof traverseJsonSchema>) | ||
.mockImplementationOnce( | ||
( | ||
params: TraverseJsonSchemaParams, | ||
callback: (params: TraverseJsonSchemaCallbackParams) => void, | ||
) => { | ||
callback({ | ||
jsonPointer: params.jsonPointer ?? '', | ||
parentJsonPointer: params.jsonPointer, | ||
parentSchema: schemaFixture, | ||
schema: subSchemaFixture, | ||
}); | ||
callback({ | ||
jsonPointer: params.jsonPointer ?? '', | ||
parentJsonPointer: params.jsonPointer, | ||
parentSchema: schemaFixture, | ||
schema: subSchemaFixture, | ||
}); | ||
}, | ||
) | ||
.mockImplementationOnce(() => undefined); | ||
|
||
derefMock.mockResolvedValueOnce(dereferencedSchemaFixture); | ||
|
||
result = await dereferenceJsonSchema( | ||
derefMock, | ||
schemaFixture, | ||
referenceMapFixture, | ||
uriOptionsFixture, | ||
); | ||
}); | ||
|
||
afterAll(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should call getJsonSchemaBaseUri()', () => { | ||
expect(getJsonSchemaBaseUri).toHaveBeenCalledTimes(2); | ||
expect(getJsonSchemaBaseUri).toHaveBeenNthCalledWith( | ||
1, | ||
schemaFixture, | ||
uriOptionsFixture, | ||
); | ||
expect(getJsonSchemaBaseUri).toHaveBeenNthCalledWith( | ||
2, | ||
dereferencedSchemaFixture, | ||
uriOptionsFixture, | ||
); | ||
}); | ||
|
||
it('should call traverseJsonSchema()', () => { | ||
const expectedFirstParams: TraverseJsonSchemaParams = { | ||
schema: schemaFixture, | ||
}; | ||
|
||
const expectedSecondParams: TraverseJsonSchemaParams = { | ||
schema: dereferencedSchemaFixture, | ||
}; | ||
|
||
expect(traverseJsonSchema).toHaveBeenCalledTimes(2); | ||
expect(traverseJsonSchema).toHaveBeenNthCalledWith( | ||
1, | ||
expectedFirstParams, | ||
expect.any(Function), | ||
); | ||
expect(traverseJsonSchema).toHaveBeenNthCalledWith( | ||
2, | ||
expectedSecondParams, | ||
expect.any(Function), | ||
); | ||
}); | ||
|
||
it('should push references', () => { | ||
expect([...referenceMapFixture.entries()]).toStrictEqual([ | ||
[subSchemaFixture.$ref, dereferencedSchemaFixture], | ||
]); | ||
}); | ||
|
||
it('should resolve to undefined', () => { | ||
expect(result).toBeUndefined(); | ||
}); | ||
}); | ||
}); |
64 changes: 64 additions & 0 deletions
64
...ges/parser/json-schema-parser/src/jsonSchema/202012/calculations/dereferenceJsonSchema.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { JsonSchema } from '@cuaklabs/json-schema-types/2020-12'; | ||
import { Uri } from '@cuaklabs/uri'; | ||
|
||
import { traverseJsonSchema } from '../actions/traverseJsonSchema'; | ||
import { DereferenceFunction } from '../models/DereferenceFunction'; | ||
import { TraverseJsonSchemaCallbackParams } from '../models/TraverseJsonSchemaCallbackParams'; | ||
import { UriOptions } from '../models/UriOptions'; | ||
import { getJsonSchemaBaseUri } from './getJsonSchemaBaseUri'; | ||
|
||
export async function dereferenceJsonSchema( | ||
deref: DereferenceFunction, | ||
schema: JsonSchema, | ||
referenceMap: Map<string, JsonSchema>, | ||
uriOptions: UriOptions | undefined, | ||
): Promise<void> { | ||
const baseUri: string = getJsonSchemaBaseUri(schema, uriOptions); | ||
|
||
const schemaUris: string[] = [...new Set(getSchemaUris(schema, baseUri))]; | ||
|
||
const missingSchemaUris: string[] = schemaUris.filter( | ||
(schemaUri: string) => !referenceMap.has(schemaUri), | ||
); | ||
|
||
await Promise.all( | ||
missingSchemaUris.map(async (schemaUri: string): Promise<void> => { | ||
const dereferencedSchema: JsonSchema = await deref( | ||
schema, | ||
baseUri, | ||
schemaUri, | ||
); | ||
|
||
referenceMap.set(schemaUri, dereferencedSchema); | ||
|
||
await dereferenceJsonSchema( | ||
deref, | ||
dereferencedSchema, | ||
referenceMap, | ||
uriOptions, | ||
); | ||
}), | ||
); | ||
} | ||
|
||
function getSchemaUris(schema: JsonSchema, baseUri: string): string[] { | ||
const schemaUris: string[] = []; | ||
|
||
traverseJsonSchema( | ||
{ | ||
schema, | ||
}, | ||
(params: TraverseJsonSchemaCallbackParams): void => { | ||
if ( | ||
typeof params.schema !== 'boolean' && | ||
params.schema.$ref !== undefined | ||
) { | ||
const refUri: string = new Uri(params.schema.$ref, baseUri).toString(); | ||
|
||
schemaUris.push(refUri); | ||
} | ||
}, | ||
); | ||
|
||
return schemaUris; | ||
} |
7 changes: 2 additions & 5 deletions
7
...ages/parser/json-schema-parser/src/jsonSchema/202012/calculations/getJsonSchemaBaseUri.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
packages/parser/json-schema-parser/src/jsonSchema/202012/models/DereferenceFunction.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { JsonSchema } from '@cuaklabs/json-schema-types/2020-12'; | ||
|
||
export type DereferenceFunction = ( | ||
schema: JsonSchema, | ||
baseUri: string, | ||
uri: string, | ||
) => Promise<JsonSchema>; |
4 changes: 4 additions & 0 deletions
4
packages/parser/json-schema-parser/src/jsonSchema/202012/models/UriOptions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface UriOptions { | ||
encapsulatingDocumentBaseUri?: string | undefined; | ||
retrievalUri?: string | undefined; | ||
} |