Skip to content

Commit ca13b7a

Browse files
author
Phil Sturgeon
authored
Merge pull request #3 from P0lip/master
feat: implement dereferencing
2 parents 27620d7 + afdc901 commit ca13b7a

20 files changed

+233
-68
lines changed

README.md

+7-4
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ const schema = {
2929
format: 'date-time',
3030
};
3131

32-
const convertedSchema = toOpenApi(schema);
32+
(async () => {
33+
const convertedSchema = await toOpenApi(schema);
34+
console.log(convertedSchema);
35+
})();
3336

34-
console.log(convertedSchema);
3537
```
3638

3739
The example prints out
@@ -44,8 +46,6 @@ The example prints out
4446
}
4547
```
4648

47-
**NOTE**: `$ref`s are not dereferenced. Use a dereferencer such as [json-schema-ref-parser](https://www.npmjs.com/package/json-schema-ref-parser) prior to using this package.
48-
4949
### Options
5050

5151
The function accepts `options` object as the second argument.
@@ -54,6 +54,9 @@ The function accepts `options` object as the second argument.
5454

5555
If set to `false`, converts the provided schema in place. If `true`, clones the schema by converting it to JSON and back. The overhead of the cloning is usually negligible. Defaults to `true`.
5656

57+
#### `dereference` (boolean)
58+
59+
If set to `true`, all local and remote references (http/https and file) $refs will be dereferenced. Defaults to `false`.
5760

5861
## Why?
5962

index.js

+49-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1+
'use strict';
2+
13
const schemaWalker = require('@cloudflare/json-schema-walker');
4+
const { Resolver } = require('@stoplight/json-ref-resolver');
5+
const { parse } = require('@stoplight/yaml');
6+
const fetch = require('node-fetch');
7+
const fs = require('fs');
8+
const readFileAsync = require('util').promisify(fs.readFile);
29

310
function InvalidTypeError(message) {
411
this.name = 'InvalidTypeError';
@@ -7,13 +14,17 @@ function InvalidTypeError(message) {
714

815
InvalidTypeError.prototype = new Error();
916

10-
function convert(schema, options = {}) {
11-
const { cloneSchema = true } = options;
17+
async function convert(schema, options = {}) {
18+
const { cloneSchema = true, dereference = false } = options;
1219

1320
if (cloneSchema) {
1421
schema = JSON.parse(JSON.stringify(schema));
1522
}
1623

24+
if (dereference) {
25+
({ result: schema } = await resolver.resolve(schema));
26+
}
27+
1728
const vocab = schemaWalker.getVocabulary(schema, schemaWalker.vocabularies.DRAFT_04);
1829
schemaWalker.schemaWalk(schema, convertSchema, null, vocab);
1930
return schema;
@@ -32,8 +43,8 @@ function convertSchema(schema, path, parent, parentPath) {
3243
schema = rewriteConst(schema);
3344
schema = convertDependencies(schema);
3445
schema = rewriteIfThenElse(schema);
35-
schema = rewriteExclusiveMinMax(schema);
36-
schema = convertExamples(schema);
46+
schema = rewriteExclusiveMinMax(schema);
47+
schema = convertExamples(schema);
3748

3849
if (typeof schema['patternProperties'] === 'object') {
3950
schema = convertPatternProperties(schema);
@@ -200,4 +211,38 @@ function rewriteExclusiveMinMax(schema) {
200211
return schema;
201212
}
202213

214+
const httpReader = {
215+
async resolve(ref) {
216+
return (await fetch(String(ref))).text();
217+
},
218+
};
219+
220+
const resolver = new Resolver({
221+
resolvers: {
222+
http: httpReader,
223+
https: httpReader,
224+
file: {
225+
resolve(ref) {
226+
return readFileAsync(ref.path(), 'utf8');
227+
},
228+
},
229+
},
230+
231+
transformDereferenceResult(opts) {
232+
// this should not be needed, but unfortunately is due to a quirk in json-ref-resolver returning sealed object
233+
opts.result = JSON.parse(JSON.stringify(opts.result));
234+
return opts;
235+
},
236+
237+
parseResolveResult(opts) {
238+
try {
239+
opts.result = parse(opts.result);
240+
} catch {
241+
// let's carry on
242+
}
243+
244+
return opts;
245+
},
246+
});
247+
203248
module.exports = convert;

package.json

+9-1
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,21 @@
1010
"repository": "github:openapi-js/json-schema-to-openapi-schema",
1111
"author": "Phil Sturgeon <[email protected]>",
1212
"license": "MIT",
13+
"engines": {
14+
"node": ">=8"
15+
},
1316
"devDependencies": {
1417
"coveralls": "^3.0.0",
1518
"mocha": "^5.0.0",
19+
"nock": "^11.3.6",
1620
"nyc": "^11.6.0",
1721
"should": "^13.2.0"
1822
},
1923
"dependencies": {
20-
"@cloudflare/json-schema-walker": "^0.1.1"
24+
"@cloudflare/json-schema-walker": "^0.1.1",
25+
"@stoplight/json-ref-resolver": "^2.3.0",
26+
"@stoplight/yaml": "^3.3.1",
27+
"node-fetch": "^2.6.0",
28+
"tslib": "^1.10.0"
2129
}
2230
}

test/array-items.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
const convert = require('../');
44
const should = require('should');
55

6-
it('array-items', () => {
6+
it('array-items', async () => {
77
const schema = {
88
$schema: 'http://json-schema.org/draft-04/schema#',
99
type: 'array',
1010
};
1111

12-
const result = convert(schema);
12+
const result = await convert(schema);
1313

1414
const expected = {
1515
type: 'array',

test/clone_schema.test.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
const convert = require('../');
44
const should = require('should');
55

6-
it('cloning schema by default', () => {
6+
it('cloning schema by default', async () => {
77
const schema = {
88
$schema: 'http://json-schema.org/draft-04/schema#',
99
type: ['string', 'null'],
1010
};
1111

12-
const result = convert(schema);
12+
const result = await convert(schema);
1313

1414
const expected = {
1515
type: 'string',
@@ -20,13 +20,13 @@ it('cloning schema by default', () => {
2020
should(result).not.deepEqual(schema, 'the schema was modified in place');
2121
});
2222

23-
it('cloning schema with cloneSchema option', () => {
23+
it('cloning schema with cloneSchema option', async () => {
2424
const schema = {
2525
$schema: 'http://json-schema.org/draft-04/schema#',
2626
type: ['string', 'null'],
2727
};
2828

29-
const result = convert(schema, { cloneSchema: true });
29+
const result = await convert(schema, { cloneSchema: true });
3030

3131
const expected = {
3232
type: 'string',
@@ -37,13 +37,13 @@ it('cloning schema with cloneSchema option', () => {
3737
should(result).not.deepEqual(schema, 'the schema was modified in place');
3838
});
3939

40-
it('direct schema modification', () => {
40+
it('direct schema modification', async () => {
4141
const schema = {
4242
$schema: 'http://json-schema.org/draft-04/schema#',
4343
type: ['string', 'null'],
4444
};
4545

46-
const result = convert(schema, { cloneSchema: false });
46+
const result = await convert(schema, { cloneSchema: false });
4747

4848
const expected = {
4949
type: 'string',

test/combination_keywords.test.js

+10-10
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
const convert = require('../');
44
const should = require('should');
55

6-
it('iterates allOfs and converts types', () => {
6+
it('iterates allOfs and converts types', async () => {
77
const schema = {
88
$schema: 'http://json-schema.org/draft-04/schema#',
99
allOf: [
@@ -28,7 +28,7 @@ it('iterates allOfs and converts types', () => {
2828
]
2929
};
3030

31-
const result = convert(schema);
31+
const result = await convert(schema);
3232

3333
const expected = {
3434
allOf: [
@@ -56,7 +56,7 @@ it('iterates allOfs and converts types', () => {
5656
should(result).deepEqual(expected, 'iterated allOfs');
5757
});
5858

59-
it('iterates anyOfs and converts types', () => {
59+
it('iterates anyOfs and converts types', async () => {
6060
const schema = {
6161
$schema: 'http://json-schema.org/draft-04/schema#',
6262
anyOf: [
@@ -86,7 +86,7 @@ it('iterates anyOfs and converts types', () => {
8686
]
8787
};
8888

89-
const result = convert(schema);
89+
const result = await convert(schema);
9090

9191
const expected = {
9292
anyOf: [
@@ -119,7 +119,7 @@ it('iterates anyOfs and converts types', () => {
119119
should(result).deepEqual(expected, 'anyOfs iterated');
120120
});
121121

122-
it('iterates oneOfs and converts types', () => {
122+
it('iterates oneOfs and converts types', async () => {
123123
const schema = {
124124
$schema: 'http://json-schema.org/draft-04/schema#',
125125
oneOf: [
@@ -147,7 +147,7 @@ it('iterates oneOfs and converts types', () => {
147147
]
148148
};
149149

150-
const result = convert(schema);
150+
const result = await convert(schema);
151151

152152
const expected = {
153153
oneOf: [
@@ -180,7 +180,7 @@ it('iterates oneOfs and converts types', () => {
180180
should(result).deepEqual(expected, 'oneOfs iterated');
181181
});
182182

183-
it('converts types in not', () => {
183+
it('converts types in not', async () => {
184184
const schema = {
185185
$schema: 'http://json-schema.org/draft-04/schema#',
186186
type: 'object',
@@ -192,7 +192,7 @@ it('converts types in not', () => {
192192
}
193193
};
194194

195-
const result = convert(schema);
195+
const result = await convert(schema);
196196

197197
const expected = {
198198
type: 'object',
@@ -209,7 +209,7 @@ it('converts types in not', () => {
209209
});
210210

211211

212-
it('nested combination keywords', () => {
212+
it('nested combination keywords', async () => {
213213
const schema = {
214214
$schema: 'http://json-schema.org/draft-04/schema#',
215215
anyOf: [
@@ -250,7 +250,7 @@ it('nested combination keywords', () => {
250250
]
251251
};
252252

253-
const result = convert(schema);
253+
const result = await convert(schema);
254254

255255
const expected = {
256256
anyOf: [

test/complex_schemas.test.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ const should = require('should');
55
const getSchema = require('./helpers').getSchema;
66

77
['basic', 'address', 'calendar'].forEach(test => {
8-
it(`converts ${test}/openapi.json`, () => {
8+
it(`converts ${test}/openapi.json`, async () => {
99
const schema = getSchema(test + '/json-schema.json');
10-
const result = convert(schema);
10+
const result = await convert(schema);
1111
const expected = getSchema(test + '/openapi.json');
1212

1313
should(result).deepEqual(expected, 'converted');
1414
});
1515

16-
it(`converting ${test}/openapi.json in place`, () => {
16+
it(`converting ${test}/openapi.json in place`, async () => {
1717
const schema = getSchema(test + '/json-schema.json');
18-
const result = convert(schema, { cloneSchema: false });
18+
const result = await convert(schema, { cloneSchema: false });
1919
const expected = getSchema(test + '/openapi.json');
2020

2121
should(schema).deepEqual(result, 'changed schema in place');

test/const.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
const convert = require('../');
44
const should = require('should');
55

6-
it('const', () => {
6+
it('const', async () => {
77
const schema = {
88
$schema: 'http://json-schema.org/draft-04/schema#',
99
type: 'string',
1010
const: 'hello'
1111
};
1212

13-
const result = convert(schema);
13+
const result = await convert(schema);
1414

1515
const expected = {
1616
type: 'string',

0 commit comments

Comments
 (0)