UNPKG

6.66 kBJavaScriptView Raw
1'use strict';
2
3module.exports = $Ref;
4
5var Pointer = require('./pointer');
6
7/**
8 * This class represents a single JSON reference and its resolved value.
9 *
10 * @constructor
11 */
12function $Ref () {
13 /**
14 * The file path or URL of the referenced file.
15 * This path is relative to the path of the main JSON schema file.
16 *
17 * This path does NOT contain document fragments (JSON pointers). It always references an ENTIRE file.
18 * Use methods such as {@link $Ref#get}, {@link $Ref#resolve}, and {@link $Ref#exists} to get
19 * specific JSON pointers within the file.
20 *
21 * @type {string}
22 */
23 this.path = undefined;
24
25 /**
26 * The resolved value of the JSON reference.
27 * Can be any JSON type, not just objects. Unknown file types are represented as Buffers (byte arrays).
28 * @type {?*}
29 */
30 this.value = undefined;
31
32 /**
33 * The {@link $Refs} object that contains this {@link $Ref} object.
34 * @type {$Refs}
35 */
36 this.$refs = undefined;
37
38 /**
39 * Indicates the type of {@link $Ref#path} (e.g. "file", "http", etc.)
40 * @type {?string}
41 */
42 this.pathType = undefined;
43}
44
45/**
46 * Determines whether the given JSON reference exists within this {@link $Ref#value}.
47 *
48 * @param {string} path - The full path being resolved, optionally with a JSON pointer in the hash
49 * @param {$RefParserOptions} options
50 * @returns {boolean}
51 */
52$Ref.prototype.exists = function (path, options) {
53 try {
54 this.resolve(path, options);
55 return true;
56 }
57 catch (e) {
58 return false;
59 }
60};
61
62/**
63 * Resolves the given JSON reference within this {@link $Ref#value} and returns the resolved value.
64 *
65 * @param {string} path - The full path being resolved, optionally with a JSON pointer in the hash
66 * @param {$RefParserOptions} options
67 * @returns {*} - Returns the resolved value
68 */
69$Ref.prototype.get = function (path, options) {
70 return this.resolve(path, options).value;
71};
72
73/**
74 * Resolves the given JSON reference within this {@link $Ref#value}.
75 *
76 * @param {string} path - The full path being resolved, optionally with a JSON pointer in the hash
77 * @param {$RefParserOptions} options
78 * @param {string} [friendlyPath] - The original user-specified path (used for error messages)
79 * @returns {Pointer}
80 */
81$Ref.prototype.resolve = function (path, options, friendlyPath) {
82 var pointer = new Pointer(this, path, friendlyPath);
83 return pointer.resolve(this.value, options);
84};
85
86/**
87 * Sets the value of a nested property within this {@link $Ref#value}.
88 * If the property, or any of its parents don't exist, they will be created.
89 *
90 * @param {string} path - The full path of the property to set, optionally with a JSON pointer in the hash
91 * @param {*} value - The value to assign
92 */
93$Ref.prototype.set = function (path, value) {
94 var pointer = new Pointer(this, path);
95 this.value = pointer.set(this.value, value);
96};
97
98/**
99 * Determines whether the given value is a JSON reference.
100 *
101 * @param {*} value - The value to inspect
102 * @returns {boolean}
103 */
104$Ref.is$Ref = function (value) {
105 return value && typeof value === 'object' && typeof value.$ref === 'string' && value.$ref.length > 0;
106};
107
108/**
109 * Determines whether the given value is an external JSON reference.
110 *
111 * @param {*} value - The value to inspect
112 * @returns {boolean}
113 */
114$Ref.isExternal$Ref = function (value) {
115 return $Ref.is$Ref(value) && value.$ref[0] !== '#';
116};
117
118/**
119 * Determines whether the given value is a JSON reference, and whether it is allowed by the options.
120 * For example, if it references an external file, then options.resolve.external must be true.
121 *
122 * @param {*} value - The value to inspect
123 * @param {$RefParserOptions} options
124 * @returns {boolean}
125 */
126$Ref.isAllowed$Ref = function (value, options) {
127 if ($Ref.is$Ref(value)) {
128 if (value.$ref.substr(0, 2) === '#/' || value.$ref === '#') {
129 // It's a JSON Pointer reference, which is always allowed
130 return true;
131 }
132 else if (value.$ref[0] !== '#' && (!options || options.resolve.external)) {
133 // It's an external reference, which is allowed by the options
134 return true;
135 }
136 }
137};
138
139/**
140 * Determines whether the given value is a JSON reference that "extends" its resolved value.
141 * That is, it has extra properties (in addition to "$ref"), so rather than simply pointing to
142 * an existing value, this $ref actually creates a NEW value that is a shallow copy of the resolved
143 * value, plus the extra properties.
144 *
145 * @example:
146 * {
147 * person: {
148 * properties: {
149 * firstName: { type: string }
150 * lastName: { type: string }
151 * }
152 * }
153 * employee: {
154 * properties: {
155 * $ref: #/person/properties
156 * salary: { type: number }
157 * }
158 * }
159 * }
160 *
161 * In this example, "employee" is an extended $ref, since it extends "person" with an additional
162 * property (salary). The result is a NEW value that looks like this:
163 *
164 * {
165 * properties: {
166 * firstName: { type: string }
167 * lastName: { type: string }
168 * salary: { type: number }
169 * }
170 * }
171 *
172 * @param {*} value - The value to inspect
173 * @returns {boolean}
174 */
175$Ref.isExtended$Ref = function (value) {
176 return $Ref.is$Ref(value) && Object.keys(value).length > 1;
177};
178
179/**
180 * Returns the resolved value of a JSON Reference.
181 * If necessary, the resolved value is merged with the JSON Reference to create a new object
182 *
183 * @example:
184 * {
185 * person: {
186 * properties: {
187 * firstName: { type: string }
188 * lastName: { type: string }
189 * }
190 * }
191 * employee: {
192 * properties: {
193 * $ref: #/person/properties
194 * salary: { type: number }
195 * }
196 * }
197 * }
198 *
199 * When "person" and "employee" are merged, you end up with the following object:
200 *
201 * {
202 * properties: {
203 * firstName: { type: string }
204 * lastName: { type: string }
205 * salary: { type: number }
206 * }
207 * }
208 *
209 * @param {object} $ref - The JSON reference object (the one with the "$ref" property)
210 * @param {*} resolvedValue - The resolved value, which can be any type
211 * @returns {*} - Returns the dereferenced value
212 */
213$Ref.dereference = function ($ref, resolvedValue) {
214 if (resolvedValue && typeof resolvedValue === 'object' && $Ref.isExtended$Ref($ref)) {
215 var merged = {};
216 Object.keys($ref).forEach(function (key) {
217 if (key !== '$ref') {
218 merged[key] = $ref[key];
219 }
220 });
221 Object.keys(resolvedValue).forEach(function (key) {
222 if (!(key in merged)) {
223 merged[key] = resolvedValue[key];
224 }
225 });
226 return merged;
227 }
228 else {
229 // Completely replace the original reference with the resolved value
230 return resolvedValue;
231 }
232};