Skip to content
This repository was archived by the owner on Jul 13, 2023. It is now read-only.

Commit fb79a2c

Browse files
authored
fix: authority parsing (#51)
* chore: bump deps * fix: run parse authority before resolving pointer
1 parent db34d0c commit fb79a2c

File tree

7 files changed

+1157
-1013
lines changed

7 files changed

+1157
-1013
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
Recursively resolves JSON pointers and remote authorities.
66

7-
- Explore the interfaces: [TSDoc](https://stoplightio.github.io/json-ref-resolver/)
87
- View the changelog: [Releases](https://github.com/stoplightio/json-ref-resolver/releases)
98

109
### Features
@@ -27,7 +26,7 @@ yarn add @stoplight/json-ref-resolver
2726

2827
### Usage
2928

30-
All relevant types and options can be found in [src/types.ts](src/types.ts) or in the TSDoc.
29+
All relevant types and options can be found in [src/types.ts](src/types.ts).
3130

3231
```ts
3332
// Import the Resolver class.

package.json

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,37 +33,35 @@
3333
},
3434
"scripts": {
3535
"build": "sl-scripts build",
36-
"build.docs": "sl-scripts build:typedoc",
3736
"commit": "git-cz",
38-
"lint": "tslint -p tsconfig.json 'src/**/*.ts'",
37+
"lint": "tslint -p .",
3938
"lint.fix": "yarn lint --fix",
4039
"release": "sl-scripts release",
41-
"release.docs": "sl-scripts release:docs",
4240
"release.dryRun": "sl-scripts release --dry-run --debug",
4341
"test": "jest",
4442
"test.prod": "yarn lint && yarn test --coverage --no-cache",
4543
"test.update": "yarn test --updateSnapshot",
4644
"test.watch": "yarn test --watch"
4745
},
4846
"dependencies": {
49-
"@stoplight/json": "^2.1.0",
47+
"@stoplight/json": "2.1.x",
48+
"@types/urijs": "1.19.1",
5049
"dependency-graph": "0.8.x",
5150
"fast-memoize": "2.x.x",
52-
"immer": "^3.1.2",
51+
"immer": "3.x.x",
5352
"lodash": "4.x.x",
5453
"urijs": "1.x.x"
5554
},
5655
"devDependencies": {
57-
"@stoplight/scripts": "5.1.0",
56+
"@stoplight/scripts": "7.0.0",
5857
"@types/jest": "^24.0.13",
59-
"@types/lodash": "4.14.132",
60-
"@types/urijs": "1.19.1",
58+
"@types/lodash": "4.14.133",
6159
"benchmark": "2.x.x",
6260
"jest": "^24.8.0",
6361
"ts-jest": "^24.0.2",
64-
"tslint": "^5.16.0",
62+
"tslint": "^5.17.0",
6563
"tslint-config-stoplight": "^1.3.0",
66-
"typescript": "3.4.5"
64+
"typescript": "3.5.1"
6765
},
6866
"lint-staged": {
6967
"*.{ts,tsx}$": [

src/__tests__/resolver.spec.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,9 +1390,9 @@ describe('resolver', () => {
13901390
test('should support `parseAuthorityResult` hook', async () => {
13911391
const data = {
13921392
markdown: '# hello',
1393-
bar: {
1394-
hello: 'world',
1395-
},
1393+
bar: `{
1394+
"hello": "world"
1395+
}`,
13961396
};
13971397

13981398
const source = {
@@ -1401,7 +1401,8 @@ describe('resolver', () => {
14011401
$ref: 'http://foo.com/foo.md',
14021402
},
14031403
bar: {
1404-
$ref: 'http://foo.com/bar.json',
1404+
// IMPORTANT: including a pointer to test that the target is parsed before looking up #/hello
1405+
$ref: 'http://foo.com/bar.json#/hello',
14051406
},
14061407
},
14071408
};
@@ -1426,7 +1427,7 @@ describe('resolver', () => {
14261427
heading1: 'hello',
14271428
};
14281429
} else {
1429-
opts.result.added = true;
1430+
opts.result = JSON.parse(opts.result);
14301431
}
14311432

14321433
return opts;
@@ -1440,10 +1441,7 @@ describe('resolver', () => {
14401441
foo: {
14411442
heading1: 'hello',
14421443
},
1443-
bar: {
1444-
hello: 'world',
1445-
added: true,
1446-
},
1444+
bar: 'world',
14471445
},
14481446
});
14491447
});
@@ -1546,8 +1544,8 @@ describe('resolver', () => {
15461544
});
15471545

15481546
expect({ ...result.errors[0], authority: undefined }).toEqual({
1549-
code: 'PARSE_AUTHORITY',
1550-
message: "Error parsing lookup result for 'http://foo/': Error: some parse error!",
1547+
code: 'RESOLVE_AUTHORITY',
1548+
message: "Error: Could not parse remote reference response for 'http://foo/' - Error: some parse error!",
15511549
pointerStack: [],
15521550
authorityStack: [],
15531551
path: ['definitions', 'foo'],

src/crawler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { pointerToPath } from '@stoplight/json';
22
import { DepGraph } from 'dependency-graph';
3-
import _get = require('lodash/get');
3+
import { get } from 'lodash';
44

55
import * as Types from './types';
66
import * as Utils from './utils';
@@ -154,7 +154,7 @@ export class ResolveCrawler implements Types.ICrawler {
154154
pointerStack.push(targetPointer);
155155

156156
// if we are partially resolving
157-
this.computeGraph(_get(this._runner.source, targetPath), targetPath as string[], targetPointer, pointerStack);
157+
this.computeGraph(get(this._runner.source, targetPath), targetPath as string[], targetPointer, pointerStack);
158158

159159
pointerStack.pop();
160160
}

src/runner.ts

Lines changed: 25 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { pathToPointer, pointerToPath, startsWith, trimStart } from '@stoplight/json';
22
import produce from 'immer';
3-
import _get = require('lodash/get');
4-
import _set = require('lodash/set');
3+
import { get, set } from 'lodash';
54
import * as URI from 'urijs';
65

76
import { Cache } from './cache';
@@ -95,7 +94,7 @@ export class ResolveRunner implements Types.IResolveRunner {
9594
jsonPointer = jsonPointer && jsonPointer.trim();
9695
if (jsonPointer && jsonPointer !== '#' && jsonPointer !== '#/') {
9796
targetPath = pointerToPath(jsonPointer);
98-
resolved.result = _get(resolved.result, targetPath);
97+
resolved.result = get(resolved.result, targetPath);
9998
}
10099

101100
if (!resolved.result) {
@@ -153,7 +152,7 @@ export class ResolveRunner implements Types.IResolveRunner {
153152
if (!resolvedTargetPath.length) {
154153
return r.resolved.result;
155154
} else {
156-
_set(draft, resolvedTargetPath, r.resolved.result);
155+
set(draft, resolvedTargetPath, r.resolved.result);
157156
}
158157
}
159158
});
@@ -181,7 +180,7 @@ export class ResolveRunner implements Types.IResolveRunner {
181180
if (!dependants.length) continue;
182181

183182
const pointerPath = pointerToPath(pointer);
184-
const val = _get(draft, pointerPath);
183+
const val = get(draft, pointerPath);
185184
for (const dependant of dependants) {
186185
// check to prevent circular references in the resulting JS object
187186
// this implementation is MUCH more performant than decycling the final object to remove circulars
@@ -201,7 +200,7 @@ export class ResolveRunner implements Types.IResolveRunner {
201200
resolved.refMap[pathToPointer(dependantPath)] = pathToPointer(pointerPath);
202201

203202
if (val) {
204-
_set(draft, dependantPath, val);
203+
set(draft, dependantPath, val);
205204
} else {
206205
resolved.errors.push({
207206
code: 'POINTER_MISSING',
@@ -221,7 +220,7 @@ export class ResolveRunner implements Types.IResolveRunner {
221220
}
222221

223222
if (targetPath) {
224-
resolved.result = _get(this._source, targetPath);
223+
resolved.result = get(this._source, targetPath);
225224
} else {
226225
resolved.result = this._source;
227226
}
@@ -288,7 +287,24 @@ export class ResolveRunner implements Types.IResolveRunner {
288287
throw new Error(`No reader defined for scheme '${ref.scheme()}' in ref ${ref.toString()}`);
289288
}
290289

291-
const result = await reader.read(ref, this.ctx);
290+
let result = await reader.read(ref, this.ctx);
291+
292+
// support custom parsers
293+
if (this.parseAuthorityResult) {
294+
try {
295+
const parsed = await this.parseAuthorityResult({
296+
authorityResult: result,
297+
result,
298+
targetAuthority: ref,
299+
parentAuthority: this.authority,
300+
parentPath: [],
301+
});
302+
303+
result = parsed.result;
304+
} catch (e) {
305+
throw new Error(`Could not parse remote reference response for '${ref.toString()}' - ${String(e)}`);
306+
}
307+
}
292308

293309
return new ResolveRunner(result, {
294310
depth: this.depth + 1,
@@ -380,46 +396,13 @@ export class ResolveRunner implements Types.IResolveRunner {
380396
: error.path;
381397

382398
if (errorPathInResult && errorPathInResult.length) {
383-
_set(lookupResult.resolved.result, errorPathInResult, val);
399+
set(lookupResult.resolved.result, errorPathInResult, val);
384400
} else if (lookupResult.resolved.result) {
385401
lookupResult.resolved.result = val;
386402
}
387403
}
388404
}
389405
}
390-
391-
// support custom parsers
392-
if (this.parseAuthorityResult) {
393-
try {
394-
// TODO: rework this to pass in an addValidation function to allow custom parsers to add their own validations
395-
// then generally re-work the error system here to be based around more flexible validations
396-
const parsed = await this.parseAuthorityResult({
397-
authorityResult: lookupResult,
398-
result: lookupResult.resolved.result,
399-
targetAuthority: ref,
400-
parentAuthority: this.authority,
401-
parentPath,
402-
});
403-
404-
// if (parsed.errors) {
405-
// TODO: as mentioned above, allow caller to add errors/validations
406-
// }
407-
408-
lookupResult.resolved.result = parsed.result;
409-
} catch (e) {
410-
// could not parse... roll back to original value
411-
lookupResult.resolved.result = val;
412-
413-
lookupResult.error = {
414-
code: 'PARSE_AUTHORITY',
415-
message: `Error parsing lookup result for '${ref.toString()}': ${String(e)}`,
416-
authority: ref,
417-
authorityStack: this.authorityStack,
418-
pointerStack,
419-
path: parentPath,
420-
};
421-
}
422-
}
423406
}
424407
}
425408

tslint.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
{
2-
"extends": ["tslint-config-stoplight"]
2+
"extends": ["tslint-config-stoplight"],
3+
"linterOptions": {
4+
"include": ["src/**/*"]
5+
}
36
}

0 commit comments

Comments
 (0)