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

Commit 733a06c

Browse files
committed
feat: add ref map to resolver graph node data
BREAKING CHANGE: resolver.graph node data is now an object that has `data` and `refMap`. ```ts export interface IGraphNodeData { /** * A map of the refs in this node, and where they point * * Example: * * ```json * { * "#/users/address": "file:///api.json/#models/address", * } * ``` */ refMap: Dictionary<string>; /** * This node's resolved data */ data?: any; } ```
1 parent de93486 commit 733a06c

File tree

6 files changed

+1064
-525
lines changed

6 files changed

+1064
-525
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@
4545
},
4646
"dependencies": {
4747
"@stoplight/json": "^3.1.2",
48+
"@stoplight/types": "^11.1.1",
4849
"@stoplight/path": "^1.3.0",
49-
"@stoplight/types": "^11.0.0",
5050
"@types/urijs": "1.x.x",
5151
"dependency-graph": "~0.8.0",
5252
"fast-memoize": "^2.5.1",

src/__tests__/resolver.spec.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as fs from 'fs';
2+
import produce from 'immer';
23
import * as _ from 'lodash';
34
import * as URI from 'urijs';
45

5-
import produce from 'immer';
66
import { Cache } from '../cache';
77
import { Resolver } from '../resolver';
88
import { defaultGetRef, ResolveRunner } from '../runner';
@@ -252,14 +252,14 @@ describe('resolver', () => {
252252
let resolved = await resolver.resolve(source);
253253
expect(resolved.result).toEqual(source);
254254

255-
source = produce(source, (next: any) => {
256-
next.hello.$ref = true;
255+
source = produce(source, (draft: any) => {
256+
draft.hello.$ref = true;
257257
});
258258
resolved = await resolver.resolve(source);
259259
expect(resolved.result).toEqual(source);
260260

261-
source = produce(source, (next: any) => {
262-
next.hello.$ref = 1;
261+
source = produce(source, (draft: any) => {
262+
draft.hello.$ref = 1;
263263
});
264264
resolved = await resolver.resolve(source);
265265
expect(resolved.result).toEqual(source);
@@ -880,9 +880,12 @@ describe('resolver', () => {
880880

881881
const resolver = new Resolver();
882882
const resolved = await resolver.resolve(source);
883-
expect(resolved.refMap).toEqual({
883+
const refMap: any = {
884884
'#/hello': '#/word',
885-
});
885+
};
886+
887+
expect(resolved.refMap).toEqual(refMap);
888+
expect(resolved.graph.getNodeData('root').refMap).toEqual(refMap);
886889
});
887890

888891
test('should point to its original target', async () => {
@@ -898,11 +901,14 @@ describe('resolver', () => {
898901

899902
const resolver = new Resolver();
900903
const resolved = await resolver.resolve(source);
901-
expect(resolved.refMap).toEqual({
904+
const refMap: any = {
902905
// word1, not word2 (which is what it ultimately resolves to)
903906
'#/hello': '#/word1',
904907
'#/word1': '#/word2',
905-
});
908+
};
909+
910+
expect(resolved.refMap).toEqual(refMap);
911+
expect(resolved.graph.getNodeData('root').refMap).toEqual(refMap);
906912
});
907913

908914
test('should handle remote authorities', async () => {
@@ -949,13 +955,15 @@ describe('resolver', () => {
949955
});
950956

951957
const resolved = await resolver.resolve(source);
952-
953-
expect(resolved.refMap).toEqual({
958+
const refMap: any = {
954959
'#/inner/data': 'custom://obj1/',
955960
'#/inner/dataInner': 'custom://obj1/#/inner/foo',
956961
'#/inner/dataInner2': '#/data2',
957962
'#/data2': 'custom://ob2/#/two',
958-
});
963+
};
964+
965+
expect(resolved.refMap).toEqual(refMap);
966+
expect(resolved.graph.getNodeData('root').refMap).toEqual(refMap);
959967
});
960968
});
961969

src/crawler.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@ export class ResolveCrawler implements Types.ICrawler {
1414
// to properly calculate the resolve target
1515
public jsonPointer?: string;
1616

17-
// @ts-ignore
1817
public readonly pointerGraph = new DepGraph<string>({ circular: true });
1918

20-
// @ts-ignore
2119
public readonly pointerStemGraph = new DepGraph<string>({ circular: true });
2220

2321
private _runner: Types.IResolveRunner;
@@ -146,7 +144,7 @@ export class ResolveCrawler implements Types.ICrawler {
146144
}
147145

148146
const targetRef = `${this._runner.baseUri.toString()}${targetPointer}`;
149-
if (!this._runner.graph.hasNode(targetRef)) this._runner.graph.addNode(targetRef);
147+
if (!this._runner.graph.hasNode(targetRef)) this._runner.graph.addNode(targetRef, { refMap: {} });
150148
if (this._runner.root !== targetRef) this._runner.graph.addDependency(this._runner.root, targetRef);
151149

152150
// register parent as a dependant of the target
@@ -166,7 +164,7 @@ export class ResolveCrawler implements Types.ICrawler {
166164
} else {
167165
// remote pointer
168166
const remoteRef = ref.toString();
169-
if (!this._runner.graph.hasNode(remoteRef)) this._runner.graph.addNode(remoteRef);
167+
if (!this._runner.graph.hasNode(remoteRef)) this._runner.graph.addNode(remoteRef, { refMap: {} });
170168
if (this._runner.root !== remoteRef) this._runner.graph.addDependency(this._runner.root, remoteRef);
171169

172170
if (this._runner.dereferenceRemote && !this._runner.atMaxUriDepth()) {

src/runner.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export class ResolveRunner implements Types.IResolveRunner {
2424
public readonly id: number;
2525
public readonly baseUri: uri.URI;
2626
public readonly uriCache: Types.ICache;
27-
public readonly graph: DepGraph<any>;
27+
public readonly graph: Types.IResolveRunner['graph'];
2828
public readonly root: string;
2929

3030
public depth: number;
@@ -70,7 +70,7 @@ export class ResolveRunner implements Types.IResolveRunner {
7070

7171
this.graph = graph;
7272
if (!this.graph.hasNode(this.root)) {
73-
this.graph.addNode(this.root);
73+
this.graph.addNode(this.root, { refMap: {}, data: this._source });
7474
}
7575

7676
if (this.baseUri && this.depth === 0) {
@@ -159,8 +159,11 @@ export class ResolveRunner implements Types.IResolveRunner {
159159
// if not, we should set on our targetPath
160160
if (!resolvedTargetPath.length) resolvedTargetPath = targetPath || [];
161161

162+
// refMap deprecated, use graph
162163
resolved.refMap[String(this.baseUri.clone().fragment(pathToPointer(resolvedTargetPath)))] = String(r.uri);
163164

165+
this._setGraphNodeEdge(String(this.root), pathToPointer(resolvedTargetPath), String(r.uri));
166+
164167
if (r.error) {
165168
resolved.errors.push(r.error);
166169
}
@@ -180,9 +183,7 @@ export class ResolveRunner implements Types.IResolveRunner {
180183
} else {
181184
set(draft, resolvedTargetPath, r.resolved.result);
182185

183-
if (this.graph.hasNode(String(r.uri))) {
184-
this.graph.setNodeData(String(r.uri), r.resolved.result);
185-
}
186+
this._setGraphNodeData(String(r.uri), r.resolved.result);
186187
}
187188
}
188189
});
@@ -223,14 +224,15 @@ export class ResolveRunner implements Types.IResolveRunner {
223224
// TODO: we might want to track and expose these circulars in the future?
224225
if (isCircular) continue;
225226

227+
// refMap deprecated, use graph
226228
resolved.refMap[pathToPointer(dependantPath)] = pathToPointer(pointerPath);
227229

230+
this._setGraphNodeEdge(this.root, pathToPointer(dependantPath), pathToPointer(pointerPath));
231+
228232
if (val !== void 0) {
229233
set(draft, dependantPath, val);
230234

231-
if (this.graph.hasNode(pathToPointer(pointerPath))) {
232-
this.graph.setNodeData(pathToPointer(pointerPath), original(val));
233-
}
235+
this._setGraphNodeData(pathToPointer(pointerPath), original(val));
234236
} else {
235237
resolved.errors.push({
236238
code: 'POINTER_MISSING',
@@ -289,6 +291,8 @@ export class ResolveRunner implements Types.IResolveRunner {
289291
}
290292
}
291293

294+
this._setGraphNodeData(this.root, this._source);
295+
292296
return resolved;
293297
}
294298

@@ -535,4 +539,24 @@ export class ResolveRunner implements Types.IResolveRunner {
535539

536540
return false;
537541
}
542+
543+
private _setGraphNodeData(nodeId: string, data: any) {
544+
if (!this.graph.hasNode(nodeId)) return;
545+
546+
const graphNodeData = this.graph.getNodeData(nodeId) || {};
547+
graphNodeData.data = data;
548+
549+
this.graph.setNodeData(nodeId, graphNodeData);
550+
}
551+
552+
private _setGraphNodeEdge(nodeId: string, fromPointer: string, toNodeId: string) {
553+
if (!this.graph.hasNode(nodeId)) return;
554+
555+
const graphNodeData = this.graph.getNodeData(nodeId) || {};
556+
graphNodeData.refMap = graphNodeData.refMap || {};
557+
558+
graphNodeData.refMap[fromPointer] = toNodeId;
559+
560+
this.graph.setNodeData(nodeId, graphNodeData);
561+
}
538562
}

src/types.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Segment } from '@stoplight/types';
1+
import { Dictionary, Segment } from '@stoplight/types';
22
import { DepGraph } from 'dependency-graph';
33

44
/**
@@ -113,7 +113,7 @@ export interface IResolveResult {
113113
* A graph of every single reference in source.
114114
*
115115
*/
116-
graph: DepGraph<any>;
116+
graph: DepGraph<IGraphNodeData>;
117117

118118
/** Any errors that occured during the resolution process. */
119119
errors: IResolveError[];
@@ -222,6 +222,22 @@ export interface IRefHandlerOpts {
222222
parentPointer: string;
223223
}
224224

225+
export interface IGraphNodeData {
226+
/**
227+
* A map of the refs in this node, and where they point
228+
*
229+
* Example:
230+
*
231+
* ```json
232+
* {
233+
* "#/users/address": "file:///api.json/#models/card",
234+
* }
235+
* ```
236+
*/
237+
refMap: Dictionary<string>;
238+
data?: any;
239+
}
240+
225241
export interface IResolveRunner {
226242
id: number;
227243
source: any;
@@ -231,7 +247,7 @@ export interface IResolveRunner {
231247
depth: number;
232248
baseUri: uri.URI;
233249

234-
graph: DepGraph<any>;
250+
graph: DepGraph<IGraphNodeData>;
235251
root: string;
236252

237253
atMaxUriDepth: () => boolean;

0 commit comments

Comments
 (0)